Vue 2 和 Vue 3 自定义指令的区别(面试高频必问,基于官方迁移指南 + Vue 3.4+ 最新文档)
1. 最核心区别总结(30秒回答版)
| 维度 | Vue 2(Options API 时代) | Vue 3(Composition API + 新指令 API) | 影响 |
|---|---|---|---|
| 钩子函数命名 | bind、inserted、update、componentUpdated、unbind | 重命名并对齐组件生命周期:created(新增)beforeMount(=bind)mounted(=inserted)beforeUpdate(新增)updated(=componentUpdated)beforeUnmount(新增)unmounted(=unbind) | Vue 3 更统一、更易记 |
| 移除的钩子 | update(被认为是冗余) | 彻底移除,用 updated 替代 | 减少重复代码 |
| binding 对象 | 有 binding.expression(字符串表达式) | 移除 expression,统一用 binding.value | 迁移时最容易踩坑 |
| 注册方式 | Vue.directive()(全局)directives: {}(局部) | app.directive()(全局)directives: {} 或 <script setup> 中 const vXXX = {}(局部) | Vue 3 更模块化 |
| 简化写法 | 较少支持 | 支持函数简写(同时作用于 mounted + updated) | 常用场景更简洁 |
| 其他 | 支持 vnode.context 获取实例 | 用 binding.instance 获取实例;支持 Fragment,但多根组件上指令会警告 | Vue 3 更现代 |
一句话总结:Vue 3 把指令钩子彻底对齐组件生命周期,去掉了冗余钩子,简化了 binding 对象,让代码更清晰、更容易维护。
2. 如何实现和使用一个指令(以最经典的 v-focus 自动聚焦为例)
Vue 2 写法
// 1. 全局注册(main.js)
Vue.directive('focus', {
inserted(el) { // 最常用钩子:元素插入 DOM 后
el.focus()
}
})
// 2. 局部注册(组件内)
export default {
directives: {
focus: {
inserted(el) {
el.focus()
}
}
}
}
使用:
<input v-focus />
Vue 3 写法(推荐方式)
<!-- 方式一:在 <script setup> 中直接定义(最推荐,Vue 3.2+) -->
<script setup>
const vFocus = {
mounted(el) { // 对应 Vue 2 的 inserted
el.focus()
}
}
</script>
<template>
<input v-focus />
</template>
// 方式二:全局注册(main.js / main.ts)
import { createApp } from 'vue'
const app = createApp(App)
app.directive('focus', {
mounted(el) {
el.focus()
}
})
// 方式三:局部注册(Options API 风格组件)
export default {
directives: {
focus: {
mounted(el) {
el.focus()
}
}
}
}
函数简写(当只需要 mounted + updated 时,超级简洁):
// 全局
app.directive('color', (el, binding) => {
el.style.color = binding.value // 同时在 mounted 和 updated 时执行
})
// 使用
<div v-color="textColor"></div>
3. 完整钩子对比 + 参数说明(面试手画表格必备)
每个钩子都接收 4 个参数(Vue 3):
(el, binding, vnode, prevVnode) => {}
el:真实 DOM 元素(可直接操作样式、事件、class)binding:核心对象value:传给指令的值(v-my-dir="123"→ 123)oldValue:旧值(仅 beforeUpdate / updated 有)arg:参数(v-my-dir:arg中的 arg)modifiers:修饰符对象(v-my-dir.mod1.mod2→{ mod1: true, mod2: true })instance:组件实例(Vue 3 新增)vnode/prevVnode:虚拟节点
4. 实战示例:v-debounce(防抖点击指令)(面试加分题)
Vue 3 完整实现(同时展示函数简写和对象写法):
<script setup>
const vDebounce = {
mounted(el, binding) {
let timer = null
el.addEventListener('click', () => {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
binding.value() // 执行传入的函数
}, 500)
})
}
}
</script>
<template>
<button v-debounce="handleClick">防抖按钮</button>
</template>
5. 迁移建议(从 Vue 2 升级时)
- 把
bind改成beforeMount - 把
inserted改成mounted - 把
componentUpdated改成updated - 删除所有
update钩子 - 把
binding.expression全部换成binding.value - 全局注册从
Vue.directive改成app.directive
掌握上面这套内容,面试官问“自定义指令区别”基本稳了!
需要我再给你:
- 防抖 + 节流完整对比代码
- v-lazy(图片懒加载)实战
- Vue 3 + TypeScript 写指令的写法
- 面试追问 10 题(为什么移除 update?binding.expression 为什么删?)
随时说~ 这块知识点背熟后,Vue 进阶题基本无敌!🚀