# Vue3教程 - 9 自定义指令
在前面我们使用了很多的 Vue 的内置指令来完成功能,例如 v-bind、v-show、v-text 。
我们也可以自定义指令来完成想要的功能,如果想要实现自定义指令,就要对 DOM 进行操作,其实内置指令也是这么做的。
下面通过一个简单的功能来熟悉一下自定义指令。
实现这样一个功能:
v-text 指令是将内容显示在标签中,我们编写一个功能,在内容前面加上 逗比 。
# 9.1 全局指令
在 Vue 3 中,自定义指令的定义方式与 Vue 2 略有不同。
使用 app.directive 来定义全局指令。第一个参数是指令的名称,第二个参数是一个对象,对象中定义了一些回调方法,常用的有 beforeMount、mounted、beforeUpdate、updated、beforeUnmount、unmounted。
- mounted函数会在 DOM 元素被插入到页面中的时候回调。
- beforeMount的函数是在元素加载到内存中就会回调,所以- beforeMount函数在- mounted函数之前执行。
- 回调函数的第一个参数都是绑定指令的那个元素。 
# 1 定义指令
可以在项目的入口文件 main.ts 中注册全局指令,具体实现:
import { createApp } from "vue";
import App from "./App.vue";
const app = createApp(App);
// 定义全局指令
app.directive("doubi", {
  // 在每个函数中,第一个参数都是element,表示被绑定了指令的那个DOM元素,这个element参数,是一个原生的JS对象
  // 第二个参数 binding 提供了关于指令绑定的信息。
  beforeMount(element, binding) {
    // 此时元素刚绑定了指令,还没有插入到DOM中去
    console.log("执行beforeMount函数:", element, binding);
  },
  // mounted表示元素插入到DOM中的时候会执行mounted函数,只执行一次
  mounted(element, binding) {
    console.log("执行mounted函数:", element, binding);
    element.innerText = "逗比: " + binding.value; // 初始化,前面添加上逗比
  },
  // 当模板中有数据更新的时候,即使不是当前指令用到的数据,都会执行updated,可能会触发多次
  updated(element, binding) {
    console.log("执行updated函数:", element, binding);
    element.innerText = "逗比: " + binding.value; // 数据更新后,前面添加上逗比
  },
});
app.mount("#app");
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
app.directive() 有两个参数:
- 参数1:指令的名称,注意,在定义的时候,指令的名称前面,不需要加 - v-前缀,但是在调用的时候,必须在指令名称前加上- v-前缀来进行调用。
- 参数2:是一个对象,这个对象身上有一些指令相关的回调函数,这些函数可以在特定的阶段执行相关的操作。 
指令的回调函数有两个参数:
- 第一个参数:是指令所绑定的DOM元素,它是HTMLElement类型,通过这个参数,你可以直接访问和操作DOM。例如,你可以改变元素的样式、属性、或者监听事件等。
- 第二个参数:是一个对象,它包含了关于指令的详细信息,如指令的值、参数、修饰符等。
# 2 使用指令
在组件中使用指令:
<template>
    <div>
        <!-- 2.使用指令 -->
        <div v-doubi="msg"></div>
        <!-- 其他数据 -->
        <div v-text="count"></div>
        <button @click="changeData">改变data</button>
    </div>
</template>
<!-- setup -->
<script lang="ts" setup>
import { ref } from 'vue';
let msg = ref("Hello");
let count = ref(0);
function changeData() {
    count.value++;
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
上面 v-doubi="msg" 使用指令。
需要注意:当模板中有数据更新的时候,即使不是当前指令用到的数据,都会执行 updated 。
执行效果:

# 9.2 定义私有指令
私有指令只能在当前组件中定义是使用。
以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令。
<template>
    <div>
        <!-- 2.使用指令 -->
        <div v-doubi="msg"></div>
        <!-- 其他数据 -->
        <div v-text="count"></div>
        <button @click="changeData">改变data</button>
    </div>
</template>
<!-- setup -->
<script lang="ts" setup>
import { ref } from 'vue';
let msg = ref("Hello");
let count = ref(0);
function changeData() {
    count.value++;
}
// 在模板中启用 v-focus
const vDoubi = {
      // 在每个函数中,第一个参数都是element,表示被绑定了指令的那个DOM元素,这个element参数,是一个原生的JS对象
    // 第二个参数 binding 提供了关于指令绑定的信息。
    beforeMount(element: any, binding: any) {
        // 此时元素刚绑定了指令,还没有插入到DOM中去
        console.log("执行beforeMount函数:", element, binding);
    },
    // mounted表示元素插入到DOM中的时候会执行mounted函数,只执行一次
    mounted(element: { innerText: string; }, binding: { value: string; }) {
        console.log("执行mounted函数:", element, binding);
        element.innerText = "逗比: " + binding.value; // 初始化,前面添加上逗比
    },
    // 当模板中有数据更新的时候,即使不是当前指令用到的数据,都会执行updated,可能会触发多次
    updated(element: { innerText: string; }, binding: { value: string; }) {
        console.log("执行updated函数:", element, binding);
        element.innerText = "逗比: " + binding.value; // 数据更新后,前面添加上逗比
    },
}
</script>
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
上面 vDoubi 就是一个指令,
# 9.3 指令传递参数
在上面我们创建的自定义指令,传递的参数是 data 中的参数,如果需要,还可以通过如下形式传递参数都可以
# 1 不传递参数
如果不需要参数,还可以不传递参数。
<input type="text" v-doubi>
# 2 传递字符串参数
字符串参数需要使用''括起来,因为如果不括起来,会认为是vue中定义的属性;如果是数字就不需要添加单引号了。
<input type="text" v-doubi="'green'">
# 3 传递多个参数
如果想传递多个参数可以传递一个对象:
参数传递:
<input type="text" v-doubi="{'color1':'red', 'color2':'green'}">
参数获取:
binding.value.color1
需要注意,指令回调函数的参数,除了第一个参数 element 之外,其他参数都应该是只读的,切勿进行修改。
# 9.4 指令回调钩子函数的简写
在大多数情况下,我们可能想在 mounted 和 updated 钩子上做重复动作,并且不想关心其他的钩子函数,可以简写成如下:
// 自定义全局指令
app.directive('doubi', (element, binding) => {
    console.log("执行函数:", element, binding);
    element.innerText = "逗比: " + binding.value;
});
// 自定义私有指令
// 在模板中启用 v-doubi 指令
const vDoubi = (element: HTMLElement, binding: { value: string }) => {
    console.log("执行函数:", element, binding);
    element.innerText = "逗比: " + binding.value;
};
2
3
4
5
6
7
8
9
10
11
12
简写的方式相当于在 mounted 和 updated 两个钩子方法中都写了相同的代码。
