什么是指令?
指令是控制视图的一种集成方式,可以直接对 DOM 进行操作,例如 Vue 内置的指令v-ifv-showv-for等等。
指令在 Vue 中属于底层行为,是一种数据绑定机制的产物。自定义指令是给开发者提供的一种接口,是可以操作 DOM 的,但是 Vue 不建议我们直接操作 DOM,但是某些情况下,又不得不进行操作。

例如我们想要实现一个自定义的指令v-my-show,我们首先要做的是注册自定义指令,这和注册 component 是一样的,都可以通过局部或全局进行注册:

  1. // 局部注册
  2. import myShow from "./directives/index";
  3. export default {
  4. name: "App",
  5. directives: {
  6. myShow
  7. }
  8. };
  1. // 全局注册
  2. import myShow from "./directives/index";
  3. createApp(App)
  4. .directive("my-show", myShow)
  5. .mount("#app");

以上两种方式,我们选择一种进行注册即可!

注册指令后,我们通过v-指令名称使用:

  1. <div class="box box1" v-my-show="visible1"></div>
  2. <button @click="visible1 = !visible1">Show/Hide</button>

指令构造函数

指令和组件一样,同样存在生命钩子函数,在某些时候执行:

  1. const myDirective = {
  2. // 在绑定元素的 attribute 前或事件监听器应用前调用
  3. created(el, binding, vnode, prevVnode) {},
  4. // 在元素被插入到 DOM 前调用
  5. beforeMount(el, binding, vnode, prevVnode) {},
  6. // 在绑定元素的父组件及他自己的所有子节点都挂载完成后调用
  7. mounted(el, binding, vnode, prevVnode) {},
  8. // 绑定元素的父组件更新前调用
  9. beforeUpdate(el, binding, vnode, prevVnode) {},
  10. // 在绑定元素的父组件及他自己的所有子节点都更新后调用
  11. updated(el, binding, vnode, prevVnode) {},
  12. // 绑定元素的父组件卸载前调用
  13. beforeUnmount(el, binding, vnode, prevVnode) {},
  14. // 绑定元素的父组件卸载后调用
  15. unmounted(el, binding, vnode, prevVnode) {}
  16. }
  17. export default myDirective;

可以看到以上以上 7 个钩子函数都有 4 个参数,下面对这些参数进行解释:

  • el:被绑定指令的元素,是一个 DOM 元素;
  • bindings:
    • arg: 指令的参数,例如v-my-show:abcabc就是参数;
    • dir: 当前指令对象内的所有属性,也就是以上 7 个钩子函数;
    • instance: 使用当前指令的组件实例,可以获取到组件实例中的一些数据;
    • modifiers: 自定义指令的修饰符集合,例如v-my-show.test.test就是修饰符;
    • oldValue: 更新之前指令的值,钩子函数beforeUpdateupdated才会存在该值;
    • value: 当前指令绑定的值,例如v-my-show="visible1"visible1就是指令的值;
  • vNode:绑定指令元素的虚拟节点;
  • prevNode: 上一个虚拟节点,钩子函数beforeUpdateupdated才会存在该值;

对于自定义指令来说,一个很常见的情况是仅仅需要在mountedupdated上实现相同的行为,除此之外并不需要其他钩子。这种情况下我们可以直接用一个函数来定义指令,如下所示:

  1. export default function(el, binding, vNode, prevNode){
  2. // 其他操作
  3. }

在组件上使用

当在组件上使用自定义指令时,它会始终应用于组件的根节点,和透传 attributes 类似。

  1. <MyComponent v-demo="test" />
  1. <!-- v-demo 指令会被应用在此处 -->
  2. <div>
  3. <span>My component content</span>
  4. </div>

需要注意的是组件可能含有多个根节点。当应用到一个多根组件时,指令将会被忽略且抛出一个警告。和 attribute 不同,指令不能通过 v-bind=”$attrs” 来传递给一个不同的元素。总的来说,不推荐在组件上使用自定义指令。