第 23 题:Vue3 自定义指令(directive)完整解析
🎯 一、什么是自定义指令?
自定义指令用于在DOM 层 封装可复用的逻辑,
适合处理:聚焦、懒加载、权限、拖拽、固定吸顶、长按 等场景。
Vue 提供两个层级:
- 全局指令(app.directive)
- 组件局部指令(directives: { xxx })
用途与组件不同:
- 组件 → 处理 UI 和业务逻辑
- 指令 → 只负责 DOM 操作
🎯 二、Vue3 自定义指令生命周期(重点)
Vue3 中指令共有 5 个钩子:
- created(el, binding)
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeUnmount
- unmounted
最常用:mounted 和 updated。
🎯 三、最简单示例:v-focus(输入框自动聚焦)
局部指令
xml
<input v-focus />
<script>
export default {
directives: {
focus: {
mounted(el) {
el.focus()
}
}
}
}
</script>
🎯 四、Vue3 自定义指令原理(高频面试点)
Vue 在编译模板时,会将所有指令解析为 指令对象,在渲染 DOM 时调用相应的指令钩子,对 DOM 进行增强。
执行流程:
- 模板编译成 VNode 时指令信息会被记录
- 渲染时调用
mounted(el, binding) - 更新时调用
updated(el, binding) - 销毁时调用
unmounted(el)
本质是:
VNode 生命周期 + 指令钩子 = DOM 增强
🎯 五、常见指令面试案例(重要)
1️⃣ 图片懒加载 v-lazy
scss
mounted(el, binding) {
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
el.src = binding.value
observer.unobserve(el)
}
})
observer.observe(el)
}
技术点:使用 IntersectionObserver 监听是否进入视口。
2️⃣ 权限指令 v-permission
scss
mounted(el, binding) {
const userRoles = store.state.user.roles
if (!userRoles.includes(binding.value)) {
el.remove() // 无权限则移除节点
}
}
很多后台系统都会问这个。
3️⃣ 长按指令 v-longpress
javascript
mounted(el, binding) {
let timer
el.addEventListener("mousedown", () => {
timer = setTimeout(() => binding.value(), 500)
})
el.addEventListener("mouseup", () => clearTimeout(timer))
}
🎯 六、binding 参数(面试必问)
binding 包含:
arduino
binding = {
instance, // 组件实例
value, // 传入的值
oldValue, // 旧值
arg, // 指令参数
modifiers // 修饰符
}
用法示例:
ini
<div v-demo:color.red="msg"></div>
解析:
arg = 'color'modifiers = { red: true }value = msg
🎯 七、关键知识点 - 什么时候用指令?什么时候用组件?
| 用组件? | 用指令? |
|---|---|
| 有 UI 和模板结构 | 不需要模板结构 |
| 需要逻辑复用 + DOM | 只需要封装 DOM 操作 |
| 有状态 | 无状态逻辑(纯 DOM) |
| 适合全局组件库 | 适合权限、懒加载、拖拽 |
一句话:
只要是"DOM 行为",尽量用指令;
数据逻辑 + UI 就应该用组件。
🎯 八、面试官常问追问(附最佳答案)
追问 1:Vue3 的指令和 Vue2 有哪些不同?
答:
- Vue3 去掉了
bind和inserted - 新增了
created、beforeMount - 生命周期更加统一
- binding.instance 替代 vnode.component
追问 2:指令能在 setup 中使用吗?
不能直接在 setup return 中注册,但可以写成一个对象:
javascript
const vFocus = {
mounted(el) { el.focus() }
}
return { vFocus }
追问 3:指令和 watch 哪个性能更好?
使用指令,因为:
- watch 会触发计算
- 指令只在 DOM 层操作,不依赖响应式系统
- 性能更高
追问 4:指令能否访问组件内部变量?
可以,通过 binding.instance
binding.instance.xxx
🎯 九、一句话总结
Vue3 自定义指令是操作 DOM 的利器,不参与组件逻辑,拥有独立生命周期,常用于懒加载、权限、拖拽、聚焦等场景。