Vue 2 vs Vue 3 核心差异深度解析
Vue 3 相比 Vue 2 带来了巨大的性能提升和架构改进。以下是两者在核心原理、开发体验和新特性上的主要区别。
1. 响应式系统原理 (核心区别)
这是 Vue 3 最底层的变革,直接决定了性能上限和开发体验。
Vue 2: Object.defineProperty 的局限
Vue 2 使用 ES5 的 Object.defineProperty API 来劫持对象的 getter 和 setter。
- 初始化开销大:在组件初始化时,Vue 2 必须递归遍历 data 对象的所有属性(包括嵌套属性),将它们转换为 getter/setter。如果不访问这些属性,它们依然会被转换,这在数据量大时会造成明显的性能损耗。
- 无法监听属性新增/删除:由于 JavaScript 的限制,
Object.defineProperty只能劫持已存在的属性。this.obj.newProp = 123不会触发视图更新。- 必须使用
Vue.set(this.obj, 'newProp', 123)或this.$set。
- 数组监听缺陷:Vue 2 不能通过
Object.defineProperty完美拦截数组索引的修改(如arr[0] = 1)和长度修改(arr.length = 0)。- Vue 2 不得不重写(Monkey Patch)了数组的 7 个变异方法(
push,pop,shift...)来实现响应式。
- Vue 2 不得不重写(Monkey Patch)了数组的 7 个变异方法(
Vue 3: Proxy 的强大能力
Vue 3 使用 ES6 的 Proxy API,它不是劫持属性,而是代理整个对象。
- 懒代理 (Lazy Observation):Proxy 默认只代理第一层。只有当你真正访问嵌套对象时,Vue 3 才会通过 Reflect 动态地将嵌套对象包装为 Proxy。这使得初始化速度极大提升,内存占用显著降低。
- 动态属性支持:Proxy 可以拦截对象上任意属性的变化,包括新增属性和删除属性。
state.newProp = 123直接生效。
- 数组完美支持:Proxy 可以原生拦截数组索引修改和 length 变化,无需再重写数组方法。
- 更多拦截操作:Proxy 可以拦截多达 13 种操作,包括
has(in 操作符),deleteProperty(delete 操作符) 等,这让 Vue 3 的响应式能力更全面。
深度对比表
| 特性 | Vue 2 (Object.defineProperty) | Vue 3 (Proxy) |
|---|---|---|
| 拦截粒度 | 属性级别 (必须遍历所有 key) | 对象级别 (直接拦截整个对象) |
| 初始化性能 | 递归遍历,开销大 | 懒代理,按需转换,快 |
| 新增/删除属性 | 不支持 (需用 $set / $delete) | 原生支持 |
| 数组索引修改 | 不支持 (需用 $set 或 splice) | 原生支持 |
| Map / Set 支持 | 不支持 | 支持 |
| Class 语法 | 支持较弱 | 支持 |
| 兼容性 | IE9+ | 不支持 IE11 (因 Proxy 无法被 Polyfill) |
伪代码原理解析
为了让你理解得更透彻,这里是两者实现的简化逻辑对比:
Vue 2 实现逻辑 (简化):
function defineReactive(obj, key, val) {
// 必须针对每一个 key 进行定义
Object.defineProperty(obj, key, {
get() {
// 收集依赖
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal;
// 触发更新
updateView();
}
}
});
}
// 初始化时必须立即递归遍历所有属性
for (const key in data) {
defineReactive(data, key, data[key]);
}
Vue 3 实现逻辑 (简化):
const handler = {
get(target, key, receiver) {
// 收集依赖 (track)
const result = Reflect.get(target, key, receiver);
// 懒代理:只有当属性值是对象且被访问时,才递归创建 Proxy
return isObject(result) ? reactive(result) : result;
},
set(target, key, value, receiver) {
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
// 触发更新 (trigger)
if (oldValue !== value) {
updateView();
}
return result;
},
deleteProperty(target, key) {
// 还能拦截 delete 操作
const result = Reflect.deleteProperty(target, key);
updateView();
return result;
}
};
// 不需要预先遍历 key,直接代理整个对象
const proxy = new Proxy(data, handler);
2. API 风格:Options API vs Composition API
这是开发者感受最直观的变化。
Vue 2: Options API (选项式 API)
代码按照 data, methods, computed, watch 等选项块进行组织。
- 优点:结构清晰,适合初学者,强制规范。
- 缺点:当组件逻辑复杂时,同一功能的代码(比如“搜索功能”)会分散在 data、methods 等不同地方,导致代码难以阅读和维护("反复横跳")。
Vue 3: Composition API (组合式 API)
使用 setup() 函数(或 <script setup>),将逻辑关注点组织在一起。
- 优点:逻辑复用极强(Hooks),类似 React Hooks;同一功能的代码集中在一起,易于维护;更好的类型推导。
- 兼容性:Vue 3 依然完全支持 Options API。
代码对比
Vue 2 (Options API):
export default {
data() {
return { count: 0 };
},
methods: {
increment() {
this.count++;
}
},
mounted() {
console.log('mounted');
}
};
Vue 3 (Composition API - script setup):
<script setup>
import { ref, onMounted } from 'vue';
// 状态定义
const count = ref(0);
// 方法定义
const increment = () => {
count.value++;
};
// 生命周期
onMounted(() => {
console.log('mounted');
});
</script>
3. 生命周期钩子 (Lifecycle Hooks)
Vue 3 对生命周期钩子进行了重命名,使其更语义化,并提供了对应 Composition API 的函数。
| Vue 2 (Options API) | Vue 3 (Options API) | Vue 3 (Composition API) | 说明 |
|---|---|---|---|
beforeCreate | beforeCreate | setup() | setup 执行时机更早 |
created | created | setup() | setup 取代了这两个钩子 |
beforeMount | beforeMount | onBeforeMount | |
mounted | mounted | onMounted | |
beforeUpdate | beforeUpdate | onBeforeUpdate | |
updated | updated | onUpdated | |
beforeDestroy | beforeUnmount | onBeforeUnmount | 名称变更:销毁变成了卸载 |
destroyed | unmounted | onUnmounted | 名称变更 |
errorCaptured | errorCaptured | onErrorCaptured |
4. 多根节点 (Fragments)
- Vue 2: 组件模板必须有一个唯一的根元素。
- Vue 3: 支持多根节点(Fragments)。Vue 3 会自动在虚拟 DOM 中创建一个 Fragment 节点来包裹它们,减少了不必要的 DOM 层级嵌套。
5. TypeScript 支持
- Vue 2: 对 TypeScript 支持较弱。因为
this上下文的动态性,很难进行完美的类型推导,通常需要使用vue-class-component装饰器写法,体验较为割裂。 - Vue 3: 源码完全使用 TypeScript 重写。Composition API 天然对类型推导友好,不再依赖复杂的
this,开发体验极佳。
6. 新增内置组件与特性
Teleport (传送门)
允许将组件的 DOM 结构渲染到该组件 DOM 树之外的地方(例如直接挂载到 body 下),这对于制作模态框 (Modal)、通知 (Toast) 非常有用,避免了 z-index 层级冲突问题。
<Teleport to="body">
<div class="modal">我是挂载在 body 下的模态框</div>
</Teleport>
Suspense (实验性)
允许在组件树中协调异步依赖的处理。可以在深层组件加载完成前,在父组件渲染一个“加载中”状态。
Tree Shaking (按需打包)
Vue 3 的全局 API(如 nextTick, set 等)改为 ES 模块导出。如果你不使用某个功能,打包工具可以将其摇掉(Remove),从而减小最终包的体积。
7. 全局 API 变更
Vue 2 使用 new Vue() 并修改 Vue 全局对象,这导致在同一页面运行多个 Vue 实例时容易相互污染。Vue 3 改为使用 createApp 创建应用实例。
// Vue 3 写法
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.use(MyPlugin); // 仅影响当前实例
app.mount('#app');
总结
Vue 3 在性能(Proxy、Virtual DOM 优化)、可维护性(Composition API、TypeScript)和架构灵活性(Fragments、Teleport)上都全面优于 Vue 2。
如果是新项目,强烈建议直接使用 Vue 3 + Script Setup + TypeScript。