news 2026/6/18 14:35:36

自定义指令

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
自定义指令

文章目录

  • 前言
  • 一、什么是自定义指令
    • 1.1 定义
    • 1.2 与组件的区别
  • 二、指令钩子
    • 2.1 Vue 3 钩子
    • 2.2 Vue 2 vs Vue 3 钩子对照
    • 2.3 钩子参数
  • 三、binding 对象
    • 3.1 常用属性
    • 3.2 示例
  • 四、注册方式
    • 4.1 全局注册
    • 4.2 局部注册(script setup)
  • 五、常见指令实现
    • 5.1 v-focus:自动聚焦
    • 5.2 v-lazy:图片懒加载
    • 5.3 v-permission:权限控制
    • 5.4 v-debounce:输入防抖
    • 5.5 v-click-outside:点击外部关闭
  • 六、函数式指令
    • 6.1 简写形式
  • 七、面试聚焦
    • 7.1 Vue 3 指令钩子变化
    • 7.2 如何访问组件实例
    • 7.3 script setup 局部注册命名
  • 八、易混淆点
  • 九、思考与练习
  • 总结

前言

自定义指令用于对普通 DOM 元素进行底层操作,适合处理聚焦、懒加载、权限控制等横切逻辑。本篇会讲清楚:

  • 指令钩子与参数
  • 全局注册 vs 局部注册
  • 常见指令实现(v-focus、v-lazy、v-permission)
  • Vue 2 与 Vue 3 指令钩子变化

一、什么是自定义指令

1.1 定义

自定义指令用于在 DOM 元素挂载、更新、卸载时执行底层操作,适合直接操作 DOM的场景。

<!-- 内置指令:v-if、v-for、v-model --> <!-- 自定义指令:v-focus、v-lazy、v-permission --> <input v-focus />

1.2 与组件的区别

对比项组件自定义指令
粒度完整 UI 单元单个 DOM 元素
用途复用 UI 结构复用 DOM 行为
典型场景Button、Modal聚焦、懒加载、权限

二、指令钩子

2.1 Vue 3 钩子

constmyDirective={// 元素挂载到 DOM 后调用mounted(el,binding,vnode,prevVnode){},// 元素更新后调用(binding 变化时)updated(el,binding,vnode,prevVnode){},// 元素卸载前调用beforeUnmount(el,binding,vnode,prevVnode){},// 元素卸载后调用unmounted(el,binding,vnode,prevVnode){}}

2.2 Vue 2 vs Vue 3 钩子对照

Vue 2Vue 3
bindbeforeMount
insertedmounted
updatebeforeUpdate
componentUpdatedupdated
unbindunmounted

2.3 钩子参数

mounted(el,binding,vnode,prevVnode){// el:指令绑定的 DOM 元素// binding:指令绑定信息对象// vnode:Vue 虚拟节点// prevVnode:更新前的虚拟节点(仅 updated 有值)}

三、binding 对象

3.1 常用属性

mounted(el,binding){binding.value// 指令绑定的值,如 v-focus="true" 中的 truebinding.oldValue// 更新前的值(仅 updated 可用)binding.arg// 指令参数,如 v-color:red 中的 'red'binding.modifiers// 修饰符对象,如 v-click.stop 中的 { stop: true }binding.instance// 当前组件实例(Vue 3 无 this,通过此访问)binding.dir// 指令定义对象}

3.2 示例

<!-- v-demo:foo.bar="baz" --> <!-- arg: 'foo' --> <!-- modifiers: { bar: true } --> <!-- value: 'baz' --> <input v-permission:edit="['admin']" /> <!-- arg: 'edit' --> <!-- value: ['admin'] -->

四、注册方式

4.1 全局注册

// main.jsimport{createApp}from'vue'importAppfrom'./App.vue'constapp=createApp(App)// 注册指令app.directive('focus',{mounted(el){el.focus()}})app.mount('#app')
<!-- 任意组件中使用 --> <input v-focus />

4.2 局部注册(script setup)

<script setup> // 局部指令:变量名必须以 v 开头 + 驼峰 const vFocus = { mounted(el) { el.focus() } } </script> <template> <input v-focus /> </template>
<script setup> // 从外部文件导入 import { vLazy } from '@/directives/lazy' </script> <template> <img v-lazy="imageUrl" /> </template>

五、常见指令实现

5.1 v-focus:自动聚焦

constvFocus={mounted(el){el.focus()}}// 使用// <input v-focus />// <input v-focus="shouldFocus" /> 条件聚焦constvFocus={mounted(el,binding){if(binding.value!==false){el.focus()}}}

5.2 v-lazy:图片懒加载

constvLazy={mounted(el,binding){constobserver=newIntersectionObserver(([entry])=>{if(entry.isIntersecting){el.src=binding.value observer.unobserve(el)}})observer.observe(el)},unmounted(el){// 清理 observer(需在 mounted 中保存引用)}}// 使用// <img v-lazy="imageUrl" />
// 完整版(含占位图和清理)constvLazy={mounted(el,binding){el.src=binding.arg||'placeholder.jpg'// 占位图constobserver=newIntersectionObserver(([entry])=>{if(entry.isIntersecting){el.src=binding.value observer.unobserve(el)}})observer.observe(el)el._lazyObserver=observer},unmounted(el){el._lazyObserver?.unobserve(el)el._lazyObserver=null}}

5.3 v-permission:权限控制

constvPermission={mounted(el,binding){const{value}=bindingconstuserRoles=getUserRoles()// 从 store 或 inject 获取consthasPermission=value.some(role=>userRoles.includes(role))if(!hasPermission){// 无权限:移除元素或禁用el.parentNode?.removeChild(el)// 或 el.disabled = true}}}// 使用// <button v-permission="['admin']">删除</button>// <button v-permission="['admin', 'editor']">编辑</button>

5.4 v-debounce:输入防抖

constvDebounce={mounted(el,binding){constdelay=binding.arg?parseInt(binding.arg):300consthandler=binding.value// 传入的回调函数lettimer=nullel._debounceHandler=(e)=>{clearTimeout(timer)timer=setTimeout(()=>handler(e),delay)}el.addEventListener('input',el._debounceHandler)},unmounted(el){el.removeEventListener('input',el._debounceHandler)}}// 使用// <input v-debounce:500="onSearch" />

5.5 v-click-outside:点击外部关闭

constvClickOutside={mounted(el,binding){el._clickOutside=(e)=>{if(!el.contains(e.target)){binding.value()// 执行回调}}document.addEventListener('click',el._clickOutside)},unmounted(el){document.removeEventListener('click',el._clickOutside)}}// 使用:下拉菜单点击外部关闭// <div v-click-outside="closeDropdown">...</div>

六、函数式指令

6.1 简写形式

如果指令只在mountedupdated中触发相同逻辑,可以简写为函数:

// 完整写法app.directive('color',{mounted(el,binding){el.style.color=binding.value},updated(el,binding){el.style.color=binding.value}})// 函数式简写:mounted 和 updated 都会调用app.directive('color',(el,binding)=>{el.style.color=binding.value})

七、面试聚焦

7.1 Vue 3 指令钩子变化

// Vue 2: bind, inserted, update, componentUpdated, unbind// Vue 3: beforeMount, mounted, beforeUpdate, updated, beforeUnmount, unmounted// 命名与组件生命周期对齐

7.2 如何访问组件实例

// Vue 2: 指令钩子中有 this// Vue 3: 无 this,通过 binding.instance 访问mounted(el,binding){constinstance=binding.instance console.log(instance.someData)}

7.3 script setup 局部注册命名

// 变量名 vFocus → 模板中使用 v-focus// 变量名 vLazyLoad → 模板中使用 v-lazy-loadconstvFocus={mounted(el){el.focus()}}

八、易混淆点

  1. 指令无 this:Vue 3 指令钩子中没有this,通过binding.instance访问组件实例。
  2. binding.oldValue:仅在updated钩子中可用,用于对比新旧值。
  3. 局部注册命名<script setup>中变量名必须以v开头,如vFocusv-focus
  4. 指令 vs 组件:指令操作单个 DOM 元素;组件是完整 UI 单元。
  5. 记得清理:在unmounted中移除事件监听、Observer 等,避免内存泄漏。

九、思考与练习

1.自定义指令适合什么场景?

解析:需要直接操作 DOM 的横切逻辑,如聚焦、懒加载、权限控制、防抖、点击外部关闭等。不适合复杂 UI 复用(用组件)。

2.binding 对象有哪些常用属性?

解析:value(绑定值)、arg(参数)、modifiers(修饰符)、oldValue(旧值,仅 updated)、instance(组件实例)。

3.如何实现 v-focus 指令?

constvFocus={mounted(el){el.focus()}}

4.Vue 3 指令如何访问组件实例?

解析:通过binding.instance,不再有this上下文。

5.局部注册指令在 script setup 中如何命名?

解析:变量名以v开头 + 驼峰,如vFocus对应模板中的v-focus


总结

  • 自定义指令:对 DOM 元素进行底层操作,提供 mounted/updated/unmounted 钩子
  • binding:value、arg、modifiers、instance 等绑定信息
  • 注册方式:全局app.directive(),局部vXxx变量
  • 常见实现:v-focus、v-lazy、v-permission、v-debounce
  • Vue 3 变化:钩子命名对齐组件生命周期,无 this
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/18 14:34:50

变异凯撒进阶:从ASCII偏移到自定义密钥的CTF实战

1. 变异凯撒加密的CTF实战入门 第一次在CTF比赛中遇到"变异凯撒"题目时&#xff0c;我盯着那串看似随机的密文看了半天。传统的凯撒加密是固定偏移量&#xff0c;但变异凯撒就像它的名字一样——会变。就像你养了一只不听话的猫&#xff0c;每次想摸它时它都会往不同…

作者头像 李华
网站建设 2026/6/18 14:34:18

微信语音转文字的「无能」,可能是中国互联网最精明的算计

腾讯会议能精准识别说话人&#xff0c;飞书妙记能实时生成结构化纪要&#xff0c;讯飞听见的中文转写准确率突破95%——而坐拥13亿月活的微信&#xff0c;语音转文字依然一言难尽。这不是技术问题&#xff0c;这是一场预谋。一、一个诡异的断层你一定经历过这样的场景&#xff…

作者头像 李华
网站建设 2026/6/18 14:31:21

Microchip 24AA32AF与24LC32AF EEPROM选型与I2C通信实战指南

1. 项目概述&#xff1a;为什么需要一份EEPROM选型指南&#xff1f;在嵌入式开发里&#xff0c;存储配置参数、校准数据或者运行日志是家常便饭。直接存在MCU的Flash里&#xff1f;频繁擦写寿命堪忧&#xff0c;操作也麻烦。用外置的SD卡或者Flash&#xff1f;又有点杀鸡用牛刀…

作者头像 李华
网站建设 2026/6/18 14:24:18

[Android] 网页转应用v1.9

[Android] 网页转应用v1.9 链接&#xff1a;https://pan.xunlei.com/s/VOvIFw_h2C4zfHEC2x7rRPRHA1?pwdwmar# 可以将网址转换成可安装的APP应用 最新版v1.9&#xff0c;不过作者也是很久没更新了

作者头像 李华
网站建设 2026/6/18 14:20:10

MSC711xEVM评估板硬件架构深度解析:TDM、HDI16、JTAG与DDR接口实战指南

1. 项目概述&#xff1a;从芯片到系统的硬件桥梁在嵌入式DSP系统开发领域&#xff0c;尤其是涉及通信、音频处理或网络设备时&#xff0c;拿到一颗功能强大的芯片只是第一步。如何验证其性能、调试底层驱动、并最终将其功能集成到产品中&#xff0c;才是真正的挑战。这时&#…

作者头像 李华