# Vue3教程 - 9 自定义指令

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

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


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

实现这样一个功能:

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

# 9.1 全局指令

在 Vue 3 中,自定义指令的定义方式与 Vue 2 略有不同。

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

  • 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");
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

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>
1
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>
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

上面 vDoubi 就是一个指令,

# 9.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 之外,其他参数都应该是只读的,切勿进行修改。

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

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

// 自定义全局指令
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;
};
1
2
3
4
5
6
7
8
9
10
11
12

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