FORMA

Vue 2 与 Vue 3 响应式差异

Vue 2 与 Vue 3 响应式实现的核心差异如下。基础概念见 Vue 基础

以下是 Vue 2 与 Vue 3 响应式系统的核心差异分析,重点从实现原理、局限性和性能角度进行对比。

一、Vue 2 响应式原理与局限

Vue 2 使用 Object.defineProperty 对数据对象的属性进行劫持,在组件初始化时递归遍历所有属性,将其转换为 getter/setter。

1. 无法检测对象属性的新增/删除

由于 Object.defineProperty 必须在初始化时知道所有属性键名,动态添加的属性不会触发更新。

js
data() {
  return { user: { name: 'Alice' } };
},
mounted() {
  this.user.age = 25;     // ❌ 非响应式,视图不更新
  delete this.user.name;  // ❌ 非响应式
}

补救方案Vue.set / Vue.deletethis.$set / this.$delete,手动通知依赖更新。

js
this.$set(this.user, "age", 25); // ✅ 响应式
this.$delete(this.user, "name"); // ✅ 响应式

2. 无法直接监听数组索引赋值和 length 修改

Object.defineProperty 无法高效拦截数组索引变化,Vue 2 只能拦截修改数组长度的七个方法(push, pop, shift, unshift, splice, sort, reverse),而直接通过索引修改值或修改 length 无法触发更新。

js
this.list[0] = newValue; // ❌ 不触发更新
this.list.length = 0; // ❌ 不触发更新

补救方案:使用 $set 或数组变异方法(如 splice)。

js
this.$set(this.list, 0, newValue); // ✅
this.list.splice(0, 1, newValue); // ✅

3. 递归初始化的性能开销

Vue 2 在初始化 data 时会深度遍历所有嵌套对象,为每个属性添加 getter/setter。如果数据结构庞大或嵌套层级深,会消耗较多初始渲染时间,并对后续性能产生一定内存压力(每个属性都产生闭包依赖)。

二、Vue 3 响应式原理与优势

Vue 3 使用 ES6 的 Proxy + Reflect 实现响应式,从根本上解决了 Vue 2 的诸多局限。

1. 天然支持属性新增与删除

Proxy 可以拦截对对象的所有操作,包括属性读取、赋值、删除、in 运算符等。因此动态添加或删除属性会自动触发更新,无需手动 API。

js
import { reactive } from "vue";
const state = reactive({ user: { name: "Alice" } });
state.user.age = 25; // ✅ 响应式,视图更新
delete state.user.name; // ✅ 响应式

2. 完美支持数组操作

Proxy 能拦截索引赋值和 length 修改,因此直接通过索引修改数组项或修改 length 都可以触发更新,也无需对数组方法进行额外包装。

js
const list = reactive([1, 2, 3]);
list[0] = 100; // ✅ 响应式
list.length = 0; // ✅ 响应式

3. 懒递归代理(Lazy Reactivity)

Vue 3 的响应式是惰性的:当访问一个嵌套对象时,才会将该对象转换为响应式代理(Proxy),而不是初始化时就递归所有属性。这带来了明显的性能提升:

  • 初始渲染更快:仅对最外层对象创建 Proxy,深层属性只在被读取时才代理。
  • 内存占用更少:减少了不必要的 getter/setter 闭包。
  • 适合大型数据结构:例如树形数据、深度嵌套配置等。
js
const state = reactive({
  level1: {
    level2: {
      value: 42,
    },
  },
});
// 此时只有 level1 是响应式代理,level2 尚未被代理
console.log(state.level1.level2.value);
// 当访问 level2 时,Vue 3 会将其也转换为响应式代理

三、总结对比

特性Vue 2 (Object.defineProperty)Vue 3 (Proxy + Reflect)
对象属性新增/删除不支持,需 Vue.set / delete天然支持
数组索引赋值不支持,需 $set 或变异方法天然支持
修改数组 length不支持天然支持
初始化性能递归遍历所有属性,深度越大开销越大懒递归,按需代理,性能更好
代理粒度每个属性单独 getter/setter,占用内存较高每个对象一个 Proxy,内存效率更高
兼容性支持 IE9+(需 polyfill)不支持 IE(Proxy 无法完全 polyfill)
TypeScript 支持有限,需额外类型声明原生支持,类型推导更准确

四、注意事项

  • Vue 3 中 refreactive 的区别ref 用于基本类型,通过 .value 访问;reactive 用于对象/数组,基于 Proxy。
  • 解构丢失响应性:在 Vue 3 中直接解构 reactive 对象的属性会丢失响应式,需使用 toRefs 包裹。
  • Proxy 无法代理原始值:因此 Vue 3 仍需要 ref 来处理 numberstring 等类型。

总体而言,Vue 3 的响应式系统更强大、更直观、性能更优,是现代 Vue 应用的首选。

参考文献

以下链接在编写时均可正常访问:

资料说明
Vue 3:深入响应式官方深度说明
Vue 2:响应式原理Vue 2 文档(归档)
MDN:ProxyProxy

Series

reactivity

3 / 3