1. 指令钩子函数变化
生命周期 |
Vue2 钩子名 |
Vue3 钩子名 |
触发时机变化 |
初始化绑定 |
bind |
beforeMount |
执行时机相同,更名以对齐组件生命周期 |
插入DOM后 |
inserted |
mounted |
与组件 mounted 对齐 |
更新前 |
update |
beforeUpdate |
拆分原 update 逻辑,更明确阶段 |
更新后 |
componentUpdated |
updated |
确保子组件更新完成后再触发 |
解绑 |
unbind |
unmounted |
与组件卸载阶段命名一致 |
2. 钩子参数差异
参数属性 |
Vue2 访问方式 |
Vue3 访问方式 |
功能变化 |
组件实例 |
vnode.context |
binding.instance |
更直观获取当前组件实例 |
旧值对比 |
oldValue (需手动记录) |
binding.oldValue |
新增属性,自动记录上一次绑定的值 |
修饰符对象 |
modifiers |
binding.modifiers |
保持不变,支持自定义修饰符 |
动态参数 |
arg (字符串) |
binding.arg (响应式) |
支持动态参数变化监听 |
3. 动态指令参数
特性 |
Vue2 支持情况 |
Vue3 改进点 |
示例 |
动态参数名 |
支持响应式动态参数 |
支持响应式动态参数 |
v-my-dir:[dynamicArg]="value" |
类型推导 |
无类型提示 |
完整的 TypeScript 类型推导 |
在 <script setup> 中自动推断参数类型 |
参数监听 |
需手动监听变化 |
自动响应参数变化 |
参数变化触发指令更新 |
4. 性能优化
优化点 |
Vue2 实现方式 |
Vue3 优化策略 |
效果提升 |
响应式更新 |
Object.defineProperty |
Proxy 代理 |
减少不必要的依赖收集,深层监听更高效 |
虚拟 DOM 比对 |
全量 Diff |
Patch Flags 标记动态部分 |
仅更新动态绑定的节点,跳过静态内容 |
静态提升 |
无 |
编译阶段提取静态节点 |
减少运行时创建 VNode 的开销 |
指令副作用清理 |
需手动解绑事件 |
自动追踪 Effect 作用域 |
组件卸载时自动清理指令相关副作用 |
5. 片段支持(多根节点)
场景 |
Vue2 限制 |
Vue3 支持方案 |
指令影响 |
多根节点指令绑定 |
不支持,需包裹单根元素 |
Fragment 支持多根节点 |
可在多个根节点上独立使用指令 |
指令作用范围 |
仅作用于根元素 |
每个子元素可单独绑定指令 |
更灵活的指令分布 |
渲染输出 |
隐式包裹 <div> |
无额外包裹元素 |
保持 DOM 结构简洁 |
6. 自定义指令注册
注册方式 |
Vue2 方法 |
Vue3 方法 |
功能扩展 |
全局注册 |
Vue.directive() |
app.directive() |
需通过应用实例注册,避免污染全局 |
局部注册 |
组件 directives 选项 |
组件 directives 选项 |
保持局部作用域 |
组合式 API 集成 |
无法与 Composition API 结合 |
支持在 setup() 中动态生成指令 |
更灵活的指令逻辑复用 |
指令参数类型检查 |
无类型约束 |
支持 TypeScript 接口定义 |
增强代码健壮性 |
总结对比表
对比维度 |
Vue2 |
Vue3 |
核心优势 |
钩子函数 |
旧命名,更新逻辑耦合 |
对齐组件生命周期,阶段更清晰 |
更易维护,逻辑解耦 |
参数访问 |
通过 vnode 间接访问 |
直接属性注入,支持响应式旧值 |
开发体验直观,减少冗余代码 |
动态参数 |
静态字符串,无类型提示 |
响应式参数,完整类型推导 |
提升灵活性与代码质量 |
性能优化 |
手动优化为主 |
编译时 + 运行时自动优化 |
显著提升渲染效率 |
片段支持 |
单根节点限制 |
多根节点自由指令绑定 |
更贴近实际开发需求 |
指令注册 |
全局注册易污染 |
应用实例隔离 + 组合式复用 |
工程化更规范,逻辑复用性高 |
通过以上对比,Vue3 在指令系统的设计上实现了全面升级,覆盖开发效率、性能优化、类型安全等多个维度,为复杂场景提供了更强大的扩展能力。
<template>
<div class="layout">
<div v-pin:[direction]="200">{{ direction }}</div>
<button @click="direction = direction === 'top' ? 'left' : 'top'">
{{ direction }}
</button>
</div>
</template>
<script>
export default {
name: "App",
directives: {
pin: {
bind(el, binding, vnode) {
console.log("el", el);
console.log("binding", binding);
console.log("vnode", vnode);
console.log("实例", vnode.context);
el.style.position = "fixed";
const direction = binding.arg;
el.style[direction] = binding.value + "px";
},
update(el, binding) {
console.log("update", el);
console.log("binding", binding);
const direction = binding.arg;
el.style[direction] = binding.value + "px";
},
},
},
data() {
return {
direction: "top",
};
},
methods: {},
};
</script>
<template>
<div>directive</div>
<p v-highlight:[colorStyle]="'blue'">This sentence is important!</p>
<button
@click="colorStyle = colorStyle === 'color' ? 'backgroundColor' : 'color'"
>
{{ colorStyle }}
</button>
</template>
<script setup lang="ts">
import { ref } from "vue";
const colorStyle = ref("color");
const vHighlight = {
mounted: (el, binding, vnode) => {
console.log("mounted", el);
console.log("binding", binding);
console.log("vnode", vnode);
console.log("实例", binding.instance);
el.style[binding.arg] = binding.value || "red";
},
updated: (el, binding) => {
console.log("updated", el);
console.log("binding", binding);
el.style[binding.arg] = binding.value || "red";
},
};
</script>