# Vue2教程 - 11 自定义指令

在前面我们使用了很多的 Vue 的内置指令来完成功能,例如 v-bindv-showv-text

我们也可以自定义指令来完成想要的功能,如果想要实现自定义指令,就要对 DOM 进行操作,其实内置指令也是这么做的。


下面通过一个简单的功能来熟悉一下自定义指令。

实现这样一个功能:

v-text 指令是将内容显示在标签中,我们编写一个功能,在内容前面加上 逗比

# 11.1 全局指令

使用 Vue.directive 来定义全局指令,第一个参数是指令的名称,第二个参数是一个对象,对象中定义了一些回调方法,常用的有bindinsertedupdatecomponentUpdatedunbind

  • inserted 函数会在 dom 元素被插入到页面中的时候回调;
  • bind 的函数是在元素加载到内存中就会回调,所以 bind 函数在 inserted 函数之前执行。
  • 回调函数的第一个参数都是绑定指令的那个元素。

具体实现:

<!-- 结构 -->
<template>
  <div id="demo">
    <div v-text="count"></div>
    
    <!-- 2.使用指令 -->
    <div v-doubi="msg"></div>
    
    <button @click="changeData">改变data</button>
  </div>
</template>

<!-- 脚本 -->
<script>
import Vue from 'vue'

// 1.定义全局指令
Vue.directive('doubi', {
    // 在每个函数中,第一个参数都是element,表示被绑定了指令的那个DOM元素,这个element参数,是一个原生的JS对象
    
    // 每当指令绑定到元素上的时候,会立即执行这个bind 函数,只执行一次
    bind: function (element, binding) { 
        // 此时元素刚绑定了指令,还没有插入到DOM中去  
        console.log('执行bind函数:', element, binding);   
    },
    // inserted表示元素插入到DOM中的时候会执行inserted函数,只执行一次
    inserted: function (element, binding) {  
        console.log('执行inserted函数:', element, binding);
        element.innerText = "逗比: " + binding.value  // 初始化,前面添加上逗比
    },
    // 当模板中有数据更新的时候,即使不是当前指令用到的数据,都会执行updated,可能会触发多次
    update: function (element, binding) {  
      console.log('执行update函数:', element, binding.value);   
      element.innerText = "逗比: " + binding.value  // 数据更新后,前面添加上逗比
    }
});

export default {
  name: 'App',
  data() {
    return {
      msg: "Hello",
      count: 0
    }
  },
  methods: {
    changeData: function() {
      this.count++
    }
  },
}  
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

Vue.directive() 有两个参数:

  • 参数1:指令的名称,注意,在定义的时候,指令的名称前面,不需要加 v- 前缀,但是在调用的时候,必须在指令名称前加上 v- 前缀来进行调用。

  • 参数2:是一个对象,这个对象身上有一些指令相关的回调函数,这些函数可以在特定的阶段执行相关的操作。

指令的回调函数有两个参数:

  • 第一个参数:是指令所绑定的DOM元素,它是HTMLElement类型,通过这个参数,你可以直接访问和操作DOM。例如,你可以改变元素的样式、属性、或者监听事件等。
  • 第二个参数:是一个对象,它包含了关于指令的详细信息,如指令的值、参数、修饰符等。

需要注意:当模板中有数据更新的时候,即使不是当前指令用到的数据,都会执行 updated

执行效果:

# 11.2 定义私有指令

私有指令只能在当前组件中使用。

定义私有指令和定义全局指令区别不大,就是在组件实例中定义。通过 directives 属性来设置;指定指令名称和回调钩子函数。

<!-- 结构 -->
<template>
  <div id="demo">
    <!-- 2.使用指令 -->
    <div v-doubi="msg"></div>
  </div>
</template>

<!-- 脚本 -->
<script>
export default {
  name: 'App',
  data() {
    return {
      msg: "Hello",
      count: 0
    }
  },
  // 1.自定义私有指令
  directives: {
    'doubi': {
      bind: function (element, binding) {
        console.log('执行bind函数:', element, binding.value);   
      },
      inserted: function (element, binding) {
        console.log('执行函数:', element, binding.value);   
        element.innerText = "逗比: " + binding.value  // 初始化,前面添加上逗比
      },
      update: function (element, binding) {
        console.log('执行update函数:', element, binding.value);   
        element.innerText = "逗比: " + binding.value  // 数据更新后,前面添加上逗比
      }
    }
  }
}  
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 11.3 指令传递参数

在上面我们创建的自定义指令,传递的参数是 data 中的参数,如果需要,还可以通过如下形式传递参数都可以

# 1 不传递参数

如果不需要参数,还可以不传递参数。

<input type="text" v-doubi>
1

# 2 传递字符串参数

字符串参数需要使用''括起来,因为如果不括起来,会认为是vue中定义的属性;如果是数字就不需要添加单引号了。

<input type="text" v-doubi="'green'">
1

# 3 传递多个参数

如果想传递多个参数可以传递一个对象:

参数传递:

<input type="text" v-doubi="{'color1':'red', 'color2':'green'}">
1

参数获取:

binding.value.color1
1

需要注意,指令回调函数的参数,除了第一个参数 element 之外,其他参数都应该是只读的,切勿进行修改,如果需要在回调函数之间共享数据,建议通过元素的 dataset 来进行。

# 11.4 指令回调钩子函数的简写

在大多数情况下,我们可能想在 bindupdate 钩子上做重复动作,并且不想关心其他的钩子函数,可以简写成如下:

// 自定义全局指令
Vue.directive('doubi', function(element, binding) {
    element.innerText = "逗比: " + binding.value
});

// 自定义私有指令
var vm = new Vue({
    el: '#app',
    directives: { 
        // 注意:这个 function 等同于 把 代码写到了 bind 和 update 中去
        'doubi': function (element, binding) { 
            element.innerText = "逗比: " + binding.value
        }
    }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

简写的方式相当于在 bindupdate 两个钩子方法中都写了相同的代码。