Vue 自定义指令详解
Vue 自定义指令详解一、核心概念1.内置指令和自定义指令2. 作用和原理二、注册方式1. 全局注册 (app.directive)2. 局部注册 (directives 选项)三、指令钩子函数 (生命周期)1.触发时机2.常用钩子说明四、钩子函数参数五、功能强大的示例1. 带参数和修饰符的焦点指令2. 图片懒加载指令3. 权限控制指令 (简化版)六、函数简写七、对象字面量八、使用建议九、总结Vue 的自定义指令允许开发者注册可复用的指令直接作用于DOM 元素。它们是操作底层 DOM 的理想选择适用于需要直接与元素交互的场景如聚焦输入框、添加事件监听器、实现懒加载等。一、核心概念1.内置指令和自定义指令内置指令:v-model,v-show,v-if,v-for等。自定义指令: 开发者根据需求创建的以v-开头的指令用于封装特定的 DOM 操作逻辑。2. 作用和原理作用封装DOM操作/事件逻辑复用交互行为如聚焦、拖拽避免组件内直接操作DOM。原理通过指令名如v-focus绑定到元素响应生命周期钩子或值变化。二、注册方式1. 全局注册 (app.directive)在应用实例上注册所有组件均可使用。import{createApp}fromvueconstappcreateApp({})// 注册一个全局自定义指令 v-focusapp.directive(focus,{// 指令的钩子函数mounted(el){el.focus()// 聚焦元素}})2. 局部注册 (directives选项)在组件内部注册仅当前组件可用。template input v-focus / /template script export default { directives: { focus: { mounted(el) { el.focus() } } } } /script三、指令钩子函数 (生命周期)1.触发时机自定义指令提供了多个钩子函数在不同阶段执行钩子触发时机参数created指令绑定到元素后立即调用仅 Vue 3。在beforeMount之前。el, binding, vnode, prevVnodebeforeMount元素挂载前调用Vue 2中相当于bindel, binding, vnodemounted元素挂载后调用最常用el, binding, vnodebeforeUpdate元素更新前调用仅 Vue 3el, binding, vnode, prevVnodeupdated元素更新后调用Vue 2中相当于componentUpdatedel, binding, vnode, prevVnodebeforeUnmount元素卸载前调用仅 Vue 3相当于 Vue 2 的unbindel, binding, vnodeunmounted元素卸载后调用清理工作在此进行el, binding, vnode注意: Vue 2 使用bind,inserted,update,componentUpdated,unbind。Vue 3 统一为上述名称。2.常用钩子说明mounted: 最常用的钩子。元素已插入父 DOM可以安全地进行 DOM 操作如聚焦、初始化第三方库。unmounted: 必须在此处清理资源如移除事件监听器、清除定时器、销毁第三方实例防止内存泄漏。四、钩子函数参数每个钩子函数接收以下参数el: 指令绑定的元素。可以直接操作 DOM。binding: 一个对象包含以下属性value: 传递给指令的值。v-my-directive1 1则binding.value为2。oldValue: 更新前的值仅beforeUpdate和updated钩子中可用。arg: 指令的参数。v-my-directive:arg则binding.arg为arg。modifiers: 包含修饰符的对象。v-my-directive.mod1.mod2则binding.modifiers为{ mod1: true, mod2: true }。instance: 使用该指令的组件实例。dir: 指令的定义对象。vnode: 绑定元素对应的虚拟节点。prevVnode: 上一个虚拟节点仅beforeUpdate和updated中可用。五、功能强大的示例1. 带参数和修饰符的焦点指令app.directive(focus, { mounted(el, binding) { const { value, arg, modifiers } binding if (value) { // 只有值为真时才聚焦 el.focus() // 参数: 指定延迟时间 if (arg) { const delay parseInt(arg) setTimeout(() gt; el.focus(), delay) } // 修饰符: select 表示聚焦后全选文本 if (modifiers.select) { el.select() } } }, unmounted(el) { // 清理如果有必要 } })input v-focus:500.selectshouldFocus / !-- shouldFocus 为 true 时: - 延迟 500ms 后聚焦 - 聚焦后自动全选文本内容 --2. 图片懒加载指令app.directive(lazy, { mounted(el, binding) { // 创建 IntersectionObserver 实例 const observer new IntersectionObserver((entries) gt; { entries.forEach(entry gt; { if (entry.isIntersecting) { // 元素进入视口加载真实图片 el.src binding.value // 加载完成后停止观察 observer.unobserve(el) } }) }) // 开始观察元素 observer.observe(el) // 将 observer 存储在元素上便于后续清理 el._lazyObserver observer }, unmounted(el) { // 卸载时必须断开观察防止内存泄漏 if (el._lazyObserver) { el._lazyObserver.disconnect() delete el._lazyObserver } } })img v-lazyimageSrc altLazy Image /3. 权限控制指令 (简化版)// 假设有一个全局的权限检查函数 function hasPermission(permission) { // 返回用户是否有该权限 return userPermissions.includes(permission) } app.directive(permission, { mounted(el, binding) { const requiredPerm binding.value if (!hasPermission(requiredPerm)) { // 移除没有权限的元素 el.parentNode amp;amp; el.parentNode.removeChild(el) // 或者隐藏: el.style.display none } } })button v-permissiondelete_user删除用户/button !-- 如果用户无 delete_user 权限按钮将被移除 --六、函数简写如果mounted和updated钩子逻辑相同可以简写为一个函数。app.directive(color, (el, binding) gt; { // 等同于在 mounted 和 updated 钩子中执行 el.style.color binding.value })p v-colorred这段文字是红色的/p七、对象字面量如果你的指令需要多个值你可以向它传递一个 JavaScript 对象字面量。别忘了指令也可以接收任何合法的 JavaScript 表达式。template div v-demo{ color: white, text: hello! }/divapp.directive(demo,(el,binding){console.log(binding.value.color)// whiteconsole.log(binding.value.text)// hello!})八、使用建议命名: 使用有意义的、动词性的名字如v-focus,v-lazy,v-tooltip。清理资源: 在unmounted钩子中务必清理所有副作用事件监听器、定时器、观察器等。避免复杂逻辑: 指令应专注于 DOM 操作。复杂的业务逻辑应放在组件或 Composition API 中。类型安全 (TypeScript): 可以通过模块扩展为自定义指令提供类型提示。// augment.d.ts import { Directive } from vue declare module vue { export interface Directives { focus: Directive lazy: Directive } }九、总结Vue 自定义指令是操作原生 DOM 的强大工具。何时使用: 当需要直接、低级别的 DOM 操作且该操作具有通用性时。核心: 掌握钩子函数和参数(el,binding)。关键: 在unmounted钩子中进行资源清理防止内存泄漏。趋势: 在 Vue 3 Composition API 和 Teleport 等新特性下部分传统指令场景如模态框有了更现代的解决方案但指令在聚焦、懒加载、权限等场景依然不可替代。