Skip to content

自定义指令

vue 默认内置了一些指令,如v-modelv-bindv-show 等,Vue 也允许注册自定义指令。

自定义指令可以提供很好的帮助:

  • 组件权限验证
  • 文本复制
  • 快捷键绑定
  • 滚动至指定位置
  • 图片懒加载
  • 焦点

指令钩子

指令是具有一组生命周期的钩子

ts
import type { Directive, DirectiveBinding } from 'vue'

const myDirective: Directive = {
  // 在绑定元素的 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) {},
}

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • beforeMount:当指令第一次绑定到元素并且在挂载父组件之前调用。在这里你可以做一次性的初始化设置。
  • mounted:在挂载绑定元素的父组件时调用。
  • beforeUpdate:在更新包含组件的 VNode 之前调用。
  • updated:在包含组件的 VNode 及其子组件的 VNode 更新后调用。
  • beforeUnmount:在卸载绑定元素的父组件之前调用
  • unmounted:当指令与元素解除绑定且父组件已卸载时,只调用一次。

函数简写

在很多时候,你可能想在 mounted 和 updated 时触发相同行为,而不关心其它的钩子。比如这样写:

ts
import type { Directive } from 'vue'

export const accessDirective: Directive = (el, binding) => {
  const { hasAccess } = useAccess()

  if (!hasAccess(binding.value))
    el.parentNode?.removeChild(el)
}

el:指令绑定到的元素。这可用于直接操作 DOM。

binding:包含以下 property 的对象。

  • instance:使用指令的组件实例。

  • value:传递给指令的值。例如,在 v-my-directive="1 + 1" 中,该值为 2。

  • oldValue:先前的值,仅在 beforeUpdateupdated 中可用。值是否已更改都可用。

  • arg:参数传递给指令 (如果有)。例如在 v-my-directive:foo 中,arg 为 "foo"。

  • modifiers:包含修饰符 (如果有) 的对象。例如在 v-my-directive.foo.bar 中,修饰符对象为 {foo: true,bar: true}

  • dir:一个对象,在注册指令时作为参数传递。例如,在以下指令中

  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。

  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

html
<div v-demo:foo.a.b="message"></div>
<input v-model:title.capitalize="myText" />
ts
app.directive('demo', (el, binding, vnode) => {

})

注册指令

ts
import type { Directive } from 'vue'

const app = Vue.createApp({})

const accessDirective: Directive = (el, binding) => {
  const { hasAccess } = useAccess()

  if (!hasAccess(binding.value))
    el.parentNode?.removeChild(el)
}

// 注册一个全局自定义指令 `v-access`
app.directive('access', accessDirective)