1.介绍
Vue的自定义指令是一种扩展Vue模板中的HTML元素及其属性的方法。通过自定义指令,我们可以在元素上绑定自定义JavaScript行为和样式。 自定义指令在Vue应用程序中起着重要作用。 Vue内置了一些常见的指令(例如v-if,v-for等),但是出于某些业务需求可能需要编写自己的指令,以便在整个应用程序中重复使用它们。自定义指令可以让模板保持干净和简单,并提高代码的可重用性和清晰度。
2.如何实现
一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。如下vFocus
2.1注入当前组件
<script setup>
// 在模板中启用 v-focus
const vFocus = {
mounted: (el) => el.focus()
}
</script>
<template>
<input v-focus />
</template>
2.2全局注入
在实际开发中,将一个自定义指令全局注册到应用层级更常见(例如v-if,v-for等)
const app = createApp(App)
app.directive("throttle",throttleDirective)
使用app.directive来注册全局的自定义指令。throttleDirective是一个包含类似组件生命周期钩子的对象。
2.3指令的声明周期
const myDirective = {
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {
// 下面会介绍各个参数的细节
},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode, prevVnode) {}
}
2.4钩子参数
指令的钩子会传递以下几种参数:
-
el:指令绑定到的元素。这可以用于直接操作 **DOM,**可以用来获取当前绑定的DOM。
-
binding:一个对象,包含以下属性。
- value:传递给指令的值。例如在 v-throttle:mouseover = "mouseover" 中,值是 mouseover。(该mouseover是在vue文件中定义的函数)
- oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
- arg:传递给指令的参数 (如果有的话)。例如在 v-throttle:mouseover 中,参数是 "mouseover"。
- modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。
- instance:使用该指令的组件实例。
- dir:指令的定义对象。
-
vnode:代表绑定元素的底层 VNode。
-
prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。
3.实现自定义节流指令
在项目开发中,遇到些频繁触发的场景,常常需要使用节流函数来约束该行为每个某ms触发一次,防止频繁的函数调用,影响性能。自定义指令的简单实现如下:
export default {
mounted: (el: any, binding: any, vnode: any) => {
let cbFn = rafThrottle(() => {
binding.value(el);
})
el.addEventListener(
binding.arg,
cbFn
);
},
};
function rafThrottle(func: Function) {
let lock = false;
return function (...args: any[]) {
if (lock) return;
lock = true;
window.requestAnimationFrame(() => {
//@ts-ignore
func.apply(this, args);
lock = false;
});
};
}
<div v-throttle:mouseover = "mouseoverFn"></div>
其中,binding.arg获取到该指令v-throttle:mouseover中mouseover,此处的mouseover仅仅只是一个用于addEventListener的事件名称。binding.value可以获取传递给指令的参数,此处传入的是vue文件中定义好的mouseoverFn函数,制定了该节流指令需要对mouseover触发的mouseoverFn函数做约束。