vue 2 基于 object.defineproperty 实现属性级劫持,存在动态属性监听缺失、数组操作受限及初始化开销大等问题;vue 3 采用 proxy 实现对象级代理,支持新增/删除属性、完整数组拦截、惰性代理及 map/set 等数据结构,依赖收集更精细,api 更分层灵活。

Vue 响应式系统在 Vue 2 和 Vue 3 之间存在根本性差异,核心在于数据劫持方式、监听能力、初始化性能和 API 设计。Vue 1.x 已基本退出主流使用,当前重点对比集中在 Vue 2 与 Vue 3。
Vue 2:基于 Object.defineProperty 的属性级劫持
Vue 2 在组件初始化时,递归遍历 data 对象的所有已有属性,对每个属性调用 Object.defineProperty 设置 getter(用于依赖收集)和 setter(用于触发更新)。
- 只能监听已存在的属性:动态添加或删除属性(如
obj.newKey = 1或delete obj.key)不会触发更新,必须用this.$set(obj, 'key', val)或Vue.set() - 数组响应式受限:无法拦截
arr[0] = newVal或arr.length = 0,需重写 push/pop/splice 等 7 个变异方法来兜底 - 初始化开销大:无论嵌套多深、是否被模板使用,都会一次性递归代理所有属性,影响大型对象的首次渲染速度
Vue 3:基于 Proxy 的对象级代理
Vue 3 使用 Proxy 代理整个对象,配合 Reflect 实现统一拦截,从语言层面解决 Vue 2 的硬伤。
- 天然支持新增/删除属性:无需额外 API,
obj.newKey = 1或delete obj.key都能自动触发更新 - 完整监听数组操作:索引赋值(
arr[0] = 1)、length 修改、甚至arr.push()内部调用都可捕获 - 惰性代理机制:只在首次访问嵌套属性时才创建其 Proxy(例如读取
state.user.profile.name时,才代理profile),显著降低初始化负担 - 扩展性强:原生支持 Map、Set、WeakMap、WeakSet 等 ES6+ 数据结构的响应式
依赖收集与更新粒度的变化
响应式不只是“监听数据”,还涉及“谁依赖了它”以及“怎么通知它”。Vue 2 和 Vue 3 在这一环也做了关键演进。
- Vue 2:一个组件对应一个 Watcher(组件级 watcher),依赖收集发生在 render 函数执行过程中,靠 getter 触发记录;更新时整个组件重新渲染,再靠虚拟 DOM Diff 定位真实变化节点
- Vue 3:仍以组件为单位组织 effect(相当于新版 Watcher),但内部依赖追踪更精细;结合编译阶段的静态分析(如
v-once、ref标记),能跳过非响应式节点,减少无效依赖
响应式 API 的分层设计
Vue 3 将响应式能力拆解为更明确、可组合的函数,开发者可按需选用:
-
reactive():处理对象/数组,返回响应式代理(不能用于原始值) -
ref():包装任意值(含原始值),通过.value访问,模板中自动解包 -
shallowRef()和shallowReactive():仅代理第一层,适合性能敏感场景(如大数据列表) -
computed()和watch():底层均基于 effect 构建,逻辑更统一,支持更灵活的清理与暂停控制
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










