--- name: Custom Directives description: 创建可复用的指令以进行底层 DOM 操作 --- # 自定义指令 自定义指令为可复用的行为提供底层 DOM 访问能力。 ## 何时使用 在以下场景中使用自定义指令: - 需要直接操作 DOM - 该行为无法通过组件或组合式函数实现 - 需要将行为应用到原生元素上 ## 基本示例 ```vue ``` ## 指令钩子 ```ts const myDirective = { // 在元素属性/事件监听器被应用之前 created(el, binding, vnode) {}, // 在元素被插入到 DOM 之前 beforeMount(el, binding, vnode) {}, // 在元素及其子元素被挂载之后 mounted(el, binding, vnode) {}, // 在父组件更新之前 beforeUpdate(el, binding, vnode, prevVnode) {}, // 在父组件更新之后 updated(el, binding, vnode, prevVnode) {}, // 在父组件卸载之前 beforeUnmount(el, binding, vnode) {}, // 在父组件卸载之后 unmounted(el, binding, vnode) {} } ``` ## 钩子参数 ```ts interface DirectiveBinding { value: T // v-my-dir="value" oldValue: T // 旧值(仅 beforeUpdate/updated 可用) arg?: string // v-my-dir:arg modifiers: Record // v-my-dir.foo.bar → { foo: true, bar: true } instance: ComponentPublicInstance // 使用该指令的组件 dir: ObjectDirective // 指令定义对象 } ``` 用法示例: ```vue-html
``` ```ts // binding 对象: { arg: 'foo', modifiers: { bar: true }, value: /* baz 的值 */, oldValue: /* 旧值 */ } ``` ## 函数简写 当只需要 `mounted` 和 `updated` 且行为相同时: ```ts // 完整形式 const vColor = { mounted(el, binding) { el.style.color = binding.value }, updated(el, binding) { el.style.color = binding.value } } // 简写(行为相同) const vColor = (el: HTMLElement, binding: DirectiveBinding) => { el.style.color = binding.value } ``` ## 全局注册 ```ts // main.ts const app = createApp(App) app.directive('focus', { mounted: (el) => el.focus() }) // 简写 app.directive('color', (el, binding) => { el.style.color = binding.value }) ``` ## 对象字面量 传递多个值: ```vue-html
``` ```ts const vDemo = (el: HTMLElement, binding: DirectiveBinding<{ color: string; text: string }>) => { console.log(binding.value.color) // 'white' console.log(binding.value.text) // 'hello' } ``` ## 动态参数 ```vue-html
``` ## 实用示例 ### v-click-outside ```ts const vClickOutside = { mounted(el: HTMLElement, binding: DirectiveBinding<() => void>) { el._clickOutside = (event: MouseEvent) => { if (!el.contains(event.target as Node)) { binding.value() } } document.addEventListener('click', el._clickOutside) }, unmounted(el: HTMLElement) { document.removeEventListener('click', el._clickOutside) } } ``` ### v-tooltip ```ts const vTooltip = { mounted(el: HTMLElement, binding: DirectiveBinding) { el.setAttribute('title', binding.value) }, updated(el: HTMLElement, binding: DirectiveBinding) { el.setAttribute('title', binding.value) } } ``` ### v-permission ```ts const vPermission = { mounted(el: HTMLElement, binding: DirectiveBinding) { if (!hasPermission(binding.value)) { el.parentNode?.removeChild(el) } } } ``` ## TypeScript:全局指令 ```ts // directives/highlight.ts import type { Directive } from 'vue' export type HighlightDirective = Directive declare module 'vue' { export interface ComponentCustomProperties { vHighlight: HighlightDirective } } export default { mounted: (el, binding) => { el.style.backgroundColor = binding.value } } satisfies HighlightDirective ``` ## 在组件上使用 ⚠️ **不推荐**——指令作用于根元素,对于多根组件可能不可预测。 ```vue-html ```