Vue3 的自定义指令
Vue 的指令是一种在 HTML 模板中以 v-
开头的属性,像 v-show
、v-if
、v-for
之类的都属于指令。
除了使用 Vue 内置的指令外,你也可以自定义指令。自定义指令的主要功能就是操作 DOM 元素,当 Vue 的内置指令无法满足你的需求时,就可以考虑使用自定义指令。
在 Vue 的组件方法中,虽然可以直接使用 document.querySelector
获取元素操作,但是这样写的话,后期不利于维护。自定义指令可以把 DOM 操作单独拆分出来,使用的时候只需要给模板标签加 v-指令名
就可以直接操作元素,不需要给元素加 class
或 id
之类的来获取元素。
指令可以在组件中局部定义,也可以在入口文件全局定义。
局部定义
下面定义一个指令在 canvas
中画一个圆:
<template>
<div>
<canvas width="200" height="200" v-round></canvas>
</div>
</template>
<script setup>
const vRound = {
// 元素加载完成
mounted: el => {
// 设置圆的参数
el.getContext('2d').arc(100, 100, 100, 0, Math.PI * 2, false);
// 开始绘制
el.getContext('2d').stroke();
}
};
</script>
上面的指令在调用它的 canvas
元素被插入到页面后就会执行。
在 script setup
中以 v
开头的驼峰式命名的变量都可以用作自定义指令,上面的 vRound
在模板中调用的时候可以使用 v-round
,其中的 round
就是指令名称。
如果不使用 script setup
的话,自定义指令需要写在 directives
中,还是上面的在 canvas
中画圆,使用选项式的写法如下:
<template>
<div>
<canvas width="200" height="200" v-round></canvas>
</div>
</template>
<script>
export default {
directives: {
round: {
// 元素被插入到页面
mounted(el) {
// 设置圆的参数
el.getContext('2d').arc(100, 100, 100, 0, Math.PI * 2, false);
// 开始绘制
el.getContext('2d').stroke();
}
}
}
}
</script>
在 directives
中可以直接写指令名称,不需要用 v
开头的驼峰命名。
全局定义
自定义指令也可以在 main.js
入口文件中全局定义,全局定义的指令在每个组件中都能直接调用。
下面在 main.js
中定义一个元素获取焦点的指令:
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.directive('focus', {
// 元素被插入到页面
mounted(el) {
el.focus();
}
});
app.mount('#app');
下面在组件的 input
上调用这个获取焦点的指令:
<template>
<div>
<input type="text" v-focus>
</div>
</template>
使用插件定义
如果你的项目比较大或是指令比较多的话,直接在 main.js
中定义可能不太方便维护,这种情况下可以考虑使用插件在单独的 JS 文件中定义,main.js
引入指令插件注册后全局都能调用。
下面在一个 directives.js
中通过插件定义指令:
export default {
install(app) {
// 定义全局指令
app.directive('focus', {
// 元素被插入到页面
mounted(el) {
el.focus();
}
});
}
}
上面的指令功能还是在元素加载完成后获取焦点。
下面在 main.js
中引入和注册这个指令插件:
import { createApp } from 'vue';
import App from './App.vue';
import directives from './directives'; // 引入自定义指令的插件
const app = createApp(App);
// 注册指令插件
app.use(directives);
app.mount('#app');
引入注册后在每个组件都能通过 v-focus
调用。
指令钩子函数
自定义指令也包含一些钩子函数,这些函数会在不同阶段被调用,我上面的 mounted
就是在元素加载完成后调用。
下面是指令包含的钩子函数:
const vTest = {
// 在指令绑定之前调用
created() {},
// 在元素被插入到页面前调用
beforeMount() {},
// 在绑定元素的父组件和子元素都加载完成后调用
mounted() {},
// 绑定元素的父组件更新前调用
beforeUpdate() {},
// 绑定元素的父组件和子元素都更新完成后调用
updated() {},
// 绑定元素的父组件销毁前调用
beforeUnmount() {},
// 绑定元素的父组件销毁后调用
unmounted() {}
};
钩子参数
上面的钩子函数可以接收 el
、binding
、vnode
、 prevVnode
四个参数,下面是参数说明:
el
绑定的元素,可以直接操作 DOM。
binding
一个对象,其中又包含:
value
: 传递给指令的值,写法如下:
<template>
<div v-test="1 + 3"></div>
<div v-test="'hello'"></div>
<div v-test="[1, '呵呵', true]"></div>
</template>
<script setup>
const vTest = {
mounted(el, binding) {
// 在控制台输出传递给指令的值
console.log(binding.value);
}
};
</script>
上面 3 个 div
传的值在控制台输出如下:
4
hello
array [1, '呵呵', true]
oldValue
: 元素更新之前的值,只在 beforeUpdate
和 updated
中可用。
arg
: 传递给指令的参数,写法如下:
<template>
<div v-test:hello></div>
</template>
<script setup>
const vTest = {
mounted(el, binding) {
// 在控制台输出传递给指令的参数
console.log(binding.arg);
}
};
</script>
上面通过 binding.arg
获取的参数是一个 String 的 hello
。
modifiers
: 指令的修饰符,写法如下:
<template>
<div v-test.a1.a2></div>
</template>
<script setup>
const vTest = {
mounted(el, binding) {
// 在控制台输出指令修饰符
console.log(binding.modifiers);
}
};
</script>
上面的修饰符就是 a1
和 a2
,控制台输出为 {"a1": true,"a2": true}
。
instance
: 使用该指令的组件实例。
dir
: 指令的定义对象。
vnode
绑定元素的顶层 vnode,这里的 vnode 就是虚拟 DOM。
prevNode
之前绑定元素的 vnode,只在 beforeUpdate
和 updated
钩子中可用。
自定义指令的参数和值也可以动态传递,如下:
<template>
<div v-test:[arg]="value"></div>
</template>
<script setup>
const value = 'misterma.com'; // 准备传给指令的值
const arg = 'blog'; // 准备传给指令的参数
const vTest = {
mounted(el, binding) {
// 输出指令传入的值
console.log(binding.value);
// 输出指令传入的参数
console.log(binding.arg);
}
};
</script>
简化写法
如果你只需要用到指令的 mounted
和 updated
钩子的话,定义指令的时候可以直接传入一个函数,如下:
<template>
<div v-test>111</div>
</template>
<script setup>
const vTest = (el) => {
el.style.color = 'red';
};
</script>
上面的函数会在 mounted
和 updated
被调用。
版权声明:本文为原创文章,版权归 Mr. Ma's Blog 所有,转载请联系博主获得授权。
本文地址:https://www.misterma.com/archives/924/
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。