在 Vue 中,指令是用来直接编辑 DOM 的一种好方法。你肯定熟悉 vue 中的 v-if, v-show, v-bind 这些指令。
vue 自定义指令是,正如你可能猜到的那样,Vue的方式让我们为我们的项目构建其他指令。它们是在项目中添加独特和可重复使用的功能的好方法。
Vue 自定义指令是 Vue 让我们为自己的项目构建附加指令的方式。它们用来在项目中添加独特且可重用的功能。
Vue 自定义指令可以操纵元素以及 DOM 中的响应性处理。
什么是自定义指令
自定义指令是一种能让你的项目适合你需求的方法。你可能会注意到 vue 插件经常使用自定义指令。

例如,在 V-Lazy Plugin
中用了 v-lazy 指令来添加自定义功能,使图像加载更有效。使用指令是最好的情况,因为我们要直接编辑DOM。
你可能会问:难道我不能只注册 compute 和 watcher 之类的组件选项吗?
可以。 但是,尽管组件选项对于抽象和代码重用很有用,但自定义指令仍然是直接操作 DOM 元素的最好方法之一 。
一个指令有五个 hook
像组件及其生命周期 hook 一样,每个VUE指令都有自己的 hook 触发
Vue 2和 Vue 3 中,指令hook也有所不同。
以下是 Vue 2 指令 hook:
- bind – 当指令绑定到元素时调用一次
- inserted – 当绑定元素插入其父节点时
- update – 当元素更新时(但还没有任何子元素)
- componentUpdated – 子元素也更新后
- unbind – 当指令与元素解除绑定时调用一次
以下是Vue 3指令 hook:
- created – 在应用元素的属性或事件侦听器之前调用。
- beforeMount – 与 vue2 的 bind 相同
- mounted – 与 vue2 的 inserted 相同
- beforeUpdate – 在元素本身更新之前调用(如生命周期 hook)
- updated – 与 vue2 的 ComponentUpdated 相同
- beforeUnmount – 在卸载元素之前调用(如生命周期 hook)
- unmounted –与 vue2 的 unbind 相同
在实现这些钩子时,它们都有一些可以接受的参数。
- el – 指令绑定到此元素上,让你可以修改它
- binding – 一个包含很多属性的对象; 后面详细解释
- vnode – 虚拟节点
- prevVnode – 上一个虚拟节点(仅在 update hook 中提供)
Vue 指令的文档 中说明,你应该将这些参数(除了 el)看作是只读的,切勿修改。
绑定对象(Binding Object)
绑定对象包含几个属性,可帮助你为 hook 添加功能。
- name – 指令的名称(没有
v-前缀) - value – 传递给指令的值
- oldvalue – 指令之前的值(仅在 update hook 中提供)
- expression – 绑定到字符串的表达式(例如:
v-direc=”3 * 3″,expression = “3*3”) - arg – 传递给字符串的参数(例如:
V-direc:blue,arg = blue) - modifiers – 作为对象传递的所有修饰符(例如:
V-direc.blue.link,modifiers = {blue: true, link: true})
定义你的指令
在我们的 main.js 文件或者任何你定义 Vue 实例的地方——只需要使用 Vue 2 中的 Vue.directive 方法或 Vue 3 中的 app.directive 方法
现在让我们创建一个名为 v-font-size的指令,用来操纵组件的字体大小。
在 main.js 中添加一些侦听 BeForeMount 和 update 的 hook 代码并调整字体大小。
// Vue 2
Vue.directive("font-size", {
bind: (el, binding) => {
el.style.fontSize = 24 + 'px'
},
updated: (el, binding) => {
el.style.fontSize = 24 + 'px'
}
})
// Vue 3
app.directive("font-size", {
beforeMount: (el, binding) => {
el.style.fontSize = 20 + 'px'
},
updated: (el, binding) => {
el.style.fontSize = 20 + 'px'
},
})
然后在组件文件中只需要添加以下两行,就可以在操作中找我们的组件。每当我们声明指令时,都可以使用前缀 v-来访问它。
<p>Default Font Size</p>
<p v-font-size>Modified Font Size</p>
还有另一种定义 Vue 指令的方法。 也可以在 main.js. 中使用这种语法:
app.directive("font-size", (el, binding) => {
el.style.fontSize = 24 + 'px';
})
如果传递的是函数而不是对象,则它将在 bind 和 update hook 期间运行。
无论你用哪种方法,结果应该是这样:

现在完成了第一个自定义指令,下面让我们更进一步。
将参数传递给你的指令
有几种方法可以增加对指令的控制。可以通过传递额外的值、参数或修饰符来完成。
对于前面的例子,假设我们希望更好地控制元素中的字体大小。
传值 - 用于响应式数据
传递数据最直观的方式就是简单地给它一个值。 这允许指令在值被更改时响应更新,同时也提供了最灵活的控制,因为可以接受各种值(任何字体大小)。
在你的组件中,声明看起来像这样:
<p v-font-size='12'>Uses font-size directive</p>
<p v-font-size='fontSize'>Uses font-size directive</p>
在你的指令中,你需要修改方法以使用来自绑定对象的值。
app.directive("font-size", (el, binding, vnode) => {
el.style.fontSize = binding.value + 'px';
})
向指令发送参数
如果不需要任何响应性,只需要一种方法来为你的指令提供多个选项。那就是参数。
指令中的代码就是这样:
app.directive("font-size", (el, binding, vnode) => {
console.log(binding + " " + vnode);
var size = 16;
switch (binding.arg) {
case "small":
size = 8;
break;
case "large":
size = 32;
break;
default:
size = 16;
break;
}
el.style.fontSize = size + "px";
})
然后在我们的模板中:
<p v-font-size:small>Small</p>
<p v-font-size:medium>Medium</p>
<p v-font-size:large>Large</p>

使用修饰器
修饰符类似于参数,因为它们不适合响应性。 但是当与参数结合使用时,你可以创建一个高度自定义的系统。
这是因为你可以在指令上应用多个修饰器。
首先在我们的指令中实现它们。if 和 else-if 的顺序仅取决于你希望优先使用哪些修饰符。
Vue.directive("font-size", (el, binding, vnode) => {
console.log(binding + " " + vnode);
var defaultSize;
if (binding.modifiers.small) {
defaultSize = 12;
} else if (binding.modifiers.large) {
defaultSize = 32;
} else {
defaultSize = 16;
}
if (binding.modifiers.red) {
el.style.color = "#ff0000"
}
el.style.fontSize = defaultSize + "px";
})
然后可以在模板中使用:
<p v-font-size.small.red>Small</p>
<p v-font-size.medium>Medium</p>
<p v-font-size.large>Large</p>
效果如下:
