Vue3与Vue2的差异:更好的响应式数据更新
Vue 3 在响应式数据更新方面的重要变化,是以 Proxy 为基础实现 reactive(),而 Vue 2 主要依赖 Object.defineProperty()。下面保留原文的两部分对照、代码示例和总结,并修正版本边界。
一、Vue 3 的 Proxy 机制与 Vue 2 的 Object.defineProperty
Vue 2 会在初始化时递归观察已有的嵌套对象属性,并为已有属性安装 getter/setter。它也包装了数组的 push、pop、shift、unshift、splice、sort、reverse 七个变异方法,因此“Vue 2 无法监听嵌套对象和数组”并不准确。
Vue 2 真正的常见边界是:
- 初始化后直接新增或删除对象属性。
- 通过索引直接修改数组元素。
- 直接修改数组
length。
旧项目可使用 Vue.set / $set、Vue.delete / $delete 和 splice 绕过这些边界。
Vue 3 使用 Proxy 拦截目标对象上的操作,包括属性读取、写入、删除、键检查和枚举等。下面保留原文的原生 Proxy 实验,并修正为 Reflect.get() / Reflect.set():
const data = { count: 0 }
const loggingProxy = new Proxy(data, {
get(target, key, receiver) {
console.log(`正在访问属性 ${String(key)}`)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
console.log(`正在设置属性 ${String(key)} 为 ${String(value)}`)
return Reflect.set(target, key, value, receiver)
}
})
console.log(loggingProxy.count)
loggingProxy.count = 1
console.log(loggingProxy.count)
set trap 必须返回表示操作成功与否的布尔值,使用 Reflect.set() 可以正确转发默认语义及 receiver。
需要强调:这个示例只是日志代理,不是 Vue 响应式。它没有 Vue 的 track()、trigger()、effect 调度和代理缓存,不会自动让 DOM 或副作用函数更新。原生 Proxy 也只代理当前 target 一层;Vue 的深层响应式来自 reactive() 对访问到的嵌套对象继续包装。
二、Vue 3 的 reactive 与 Vue 2 的 Vue.observable
Vue 2.6 的 Vue.observable(obj) 会按 Vue 2 Observer 规则观察传入 plain object,包括初始化时已有的嵌套对象和数组。它并非“只适用于根级对象”,但仍受 Vue 2 新增/删除键、数组索引和 length 边界限制。
Vue 3 中对应的主要 API 是 reactive()。下面用 watchEffect() 证明响应式依赖会更新,而不只是赋值前后手动打印:
import { reactive, watchEffect } from 'vue'
const raw = {
count: 0,
nested: { value: 1 },
items: []
}
const state = reactive(raw)
watchEffect(() => {
console.log('effect:', state.nested.value, state.items.length)
})
state.nested.value = 2
state.items.push('Vue')
state.items[0] = 'Vue 3'
watchEffect() 会先立即执行,并在其同步读取的依赖变化后再次执行。这比普通 console.log() 更能说明 Vue 响应式更新。
使用 reactive() 时还要注意:
reactive(raw) !== raw,返回值是 Proxy。- 深层代理由 Vue 在访问嵌套对象时完成;这不是原生 Proxy 自动递归。
- 同一个 raw 对象重复传给
reactive()会得到同一代理。 - 应持续读写代理
state,不要直接修改raw并期待触发依赖。 reactive()只接受对象类型;原始值通常使用ref()。
总结
Vue 2 通过递归观察已有属性和包装七个数组变异方法实现响应式,并非完全不能观察嵌套对象或数组;它的主要缺口是对象属性新增/删除、数组索引和 length 的直接操作。
Vue 3 以 Proxy 为基础,让 reactive() 能覆盖这些操作。Proxy 只是语言层的一层拦截机制,Vue 还需要依赖收集、派发更新、深层包装及调度等完整实现。开发时应使用 reactive() 返回的代理,并用组件渲染、watch() 或 watchEffect() 消费响应式状态。
Vue 2 已结束维护,新项目建议使用 Vue 3;Vue 2 内容适用于维护历史项目。