Vue事件、修饰符、自定义指令 DforDream

一、事件处理

1. 监听事件,并触发代码

可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。

<div id="example-1">
  <button v-on:click="counter += 1">Add 1</button>
  <p>已经点击了  次.</p>
</div>

var example1 = new Vue({
  el: '#example-1',
  data: {
    counter: 0
  }
})

2. 事件处理方法

然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法名称。

<div id="example-2">
  <!-- `greet` 是在下面定义的方法名 -->
  <button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
  el: '#example-2',
  data: {
    name: 'Vue.js'
  },
  // 在 `methods` 对象中定义方法
  methods: {
    greet: function (event) {
      // `this` 在方法里指向当前 Vue 实例
      alert('Hello ' + this.name + '!')
      // `event` 是原生 DOM 事件
      if (event) {
        alert(event.target.tagName)
      }
    }
  }
})

// 也可以用 JavaScript 直接调用方法
example2.greet() // => 'Hello Vue.js!'

3. 内联处理器中的方法

<div id="example-3">
  <button v-on:click="say('hi')">Say hi</button>
  <button v-on:click="say('what')">Say what</button>
</div>
new Vue({
  el: '#example-3',
  methods: {
    say: function (message) {
      alert(message)
    }
  }
})

二、修饰符

1.事件修饰符

  • .stop - 调用 event.stopPropagation()
<body>
    <div id="app">
        <div @click="handleParentClick">
            <button @click.stop="handleClick">点击</button>
        </div>
    </div>
</body>
<script>
    let vm = new Vue({
        el:"#app",
        methods: {
            handleParentClick:function(){
                console.log("parent click")
            },
            handleClick:function(){
                console.log("child click")
            }
        },
    })
</script>
  • .prevent - 调用 event.preventDefault()
<body>
    <div id="app">
        <a href="http://www.bing.com"  @click.prevent="handleClick">点击</a>
    </div>
</body>
<script>
    let vm = new Vue({
        el:"#app",
        methods: {
            handleClick:function(){
                console.log("child click")
            }
        },
    })
</script>
  • .capture - 添加事件侦听器时使用 capture 模式。
<body>
    <div id="app">
        <div  @click.capture="handleCaptureClick">
            <button @click="handleClick">捕获模式</button>
        </div>
    </div>
</body>
<script>
    let vm = new Vue({
        el:"#app",
        methods: {
            handleCaptureClick:function(){
                console.log("捕获模式先触发");
            },
            handleClick:function(){
                console.log("child click")
            }
        },
    })
</script>
  • .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
<body>
        <div id="app">
            <div @click.self="handleParentClick">
                <button @click="handleClick">点击</button>
            </div>
        </div>
    </body>
    <script>
        let vm = new Vue({
            el:"#app",
            methods: {
                handleParentClick:function(){
                    console.log("parent click")
                },
                handleClick:function(){
                    console.log("child click")
                }
            },
        })
    </script>
  • .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。

常见的 keyAlias

.enter

.tab

.delete (捕获“删除”和“退格”键)

.esc

.space

.up

.down

.left

.right

<body>
        <div id="app">
                <!-- 13 代表 enter键 -->
                <input @keyup.enter="handleClick"></input>
  							<input @keyup.13="handleClick"></input>
        </div>
    </body>
    <script>
        let vm = new Vue({
            el:"#app",
            methods: {
                handleClick:function(){
                    console.log("键盘按上来了 enter 键")
                }
            },
        })
    </script>
  • .native - 监听组件根元素的原生事件。

  • .once - 只触发一次回调。

<body>
    <div id="app">
        <button @click.once="handleClick">点击</button>
    </div>
</body>
<script>
    let vm = new Vue({
        el: "#app",
        methods: {
            handleClick: function () {
                console.log("只执行一次")
            }
        },
    })
</script>
  • .left - (2.2.0) 只当点击鼠标左键时触发。

  • .right - (2.2.0) 只当点击鼠标右键时触发。
  • .middle - (2.2.0) 只当点击鼠标中键时触发。
        <button @click.left="handleClick">左点击</button>
        <button @click.right="handleClick">右点击</button>
        <button @click.middle="handleClick">点击鼠标中键</button>
  • .passive - (2.3.0) 以 { passive: true } 模式添加侦听器
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

这个 .passive 修饰符尤其能够提升移动端的性能。

不要把 .passive.prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你想阻止事件的默认行为。

2.按键修饰符

  <input @keydown.enter="handleKeyEvent" type="text">
  <input @keyup.enter="handleKeyEvent" type="text">

3.系统修饰符

可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。

.ctrl
.alt
.shift
.meta

在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。

<body>
    <div id="app">
         <!-- alt + click -->
        <button @click.alt="handleClick">alt+click</button>
    </div>
</body>
<script>
    let vm = new Vue({
        el: "#app",
        methods: {
            handleClick: function () {
                console.log("key event")
            }
        },
    })
</script>

4.表单修饰符

.lazy

在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步。你可以添加 lazy 修饰符,从而转为在 change 事件_之后_进行同步:

<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">

.number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

<input v-model.number="age" type="number">

这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。

.trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

<input v-model.trim="msg">

三、自定义指令

全局自定义指令

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})
// 使用
<input v-focus>

局部的自定义指令

directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus()
    }
  }
}

钩子函数

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)

update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。

componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

unbind:只调用一次,指令与元素解绑时调用。

钩子函数的参数

el:指令所绑定的元素,可以用来直接操作 DOM。

binding:一个对象,包含以下 property:

  • name:指令名,不包括 v- 前缀。
  • value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2
  • oldValue:指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用。无论值是否改变都可用。
  • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"
  • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"
  • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }

vnode:Vue 编译生成的虚拟节点。

oldVnode:上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用。

例子

<div id="hook-demo" v-demo:foo.a.b="message"></div>
 
Vue.directive('demo', {
  bind: function (el, binding, vnode) {
    var s = JSON.stringify
    el.innerHTML =
      'name: '       + s(binding.name) + '<br>' +
      'value: '      + s(binding.value) + '<br>' +
      'expression: ' + s(binding.expression) + '<br>' +
      'argument: '   + s(binding.arg) + '<br>' +
      'modifiers: '  + s(binding.modifiers) + '<br>' +
      'vnode keys: ' + Object.keys(vnode).join(', ')
  }
})

new Vue({
  el: '#hook-demo',
  data: {
    message: 'hello!'
  }
})

nextTick 与 this.$nextTick(实例方法)

Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。简单来说,Vue在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
  // DOM 更新了
})
// this.$nextTick
new Vue({
  // ...
  methods: {
    // ...
    example: function () {
      // 修改数据
      this.message = 'changed'
      // DOM 还没有更新
      this.$nextTick(function () {
        // DOM 现在更新了
        // `this` 绑定到当前实例
        this.doSomethingElse()
      })
    }
  }
})


image-20200721053214609

四、组件

可以拓展HTML元素,封装可重用的代码

1.组件基础

组件名

使用 kebab-case(推荐)

Vue.component('my-component-name', { /* ... */ })

使用 PascalCase

Vue.component('MyComponentName', { /* ... */ })

当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说 my-component-name和MyComponentName都是可接受的。注意,尽管如此,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。

组件注册

全局注册

Vue.component('my-component-name', {
  // ... 选项 ...
})

局部注册

在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件:

var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }

然后在 components 选项中定义你想要使用的组件:

new Vue({
  el: '#app',
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

组件的编写方式与Vue实例的区别

  • 自定义组件需要有一个 根节点
  • 父子组件间的data 是无法共享的
  • 组件可以有 data,methods,computed……,但是data必须是一个函数