Vue 3 不再需要 $set,根本原因是 Proxy 能天然捕获属性新增、删除和修改操作,而 Vue 2 的 Object.defineProperty 无法监听对象上原本不存在的属性赋值;Proxy 的 set 拦截器对任何属性赋值都生效,数组索引和 length 变更也能被原生监听;Vue 3 的响应式是“懒代理”,仅在首次访问时递归代理深层属性;Reflect 配合 Proxy 确保语义准确、行为可预测。

Vue 3 不再需要 $set,根本原因在于 Proxy 能天然捕获属性的新增、删除和修改操作,而 Vue 2 的 Object.defineProperty 无法监听对象上原本不存在的属性赋值行为。
Proxy 能直接拦截“添加新属性”
在 Vue 2 中,只有初始化时已存在的属性才会被 defineProperty 处理成响应式。后续动态添加的属性(如 obj.newKey = value)不会触发 getter/setter,因此视图不更新——必须用 this.$set(obj, 'newKey', value) 手动介入。
Proxy 的 set 拦截器对任何属性赋值都生效,无论该属性之前是否存在:
-
proxyObj.existing = 1→ 触发set -
proxyObj.brandNew = 2→ 同样触发set,自动完成响应式转换
数组索引和 length 变更也能被原生监听
Vue 2 对数组做了特殊处理(重写了 push、pop 等 7 个方法),但依然无法响应 arr[0] = x 或 arr.length = 0 这类操作,也得靠 $set(arr, index, value) 补救。
Proxy 的 set 和 deleteProperty 可以直接捕获:
-
proxyArr[5] = 'hello'→set拦截,触发更新 -
proxyArr.length = 2→set拦截(length 是可写属性) -
delete proxyArr[1]→deleteProperty拦截
无需递归初始化,响应式是“懒代理”的
Vue 2 创建响应式对象时,会立即递归遍历所有嵌套属性,为每一层每个 key 都调用 defineProperty,开销大且不可控。
Vue 3 的 reactive 只对顶层对象做一次 new Proxy,深层属性只在**首次被访问时**才递归代理:
-
state.user.name访问时,user才被代理 -
user.address.city访问时,address才被代理 - 没被读取的分支完全不处理,节省内存和初始化时间
Reflect 配合 Proxy 实现语义准确、可预测的行为
Vue 3 在 handler 中大量使用 Reflect.get/set/deleteProperty,不是为了炫技,而是确保代理行为与原始操作严格一致:
-
Reflect.set返回布尔值表示是否成功(比如对readonly属性赋值会返回false) -
Reflect.has正确处理in操作符和原型链查找 - 避免手写
target[key] = value导致的隐式行为偏差(例如忽略 setter、跳过原型访问等)
本质上,Proxy 提供的是对“对象行为”的完整控制权,而不仅是“属性定义”。Vue 3 把响应式从“静态声明”升级为“动态行为拦截”,$set 这种修补型 API 自然就退出了历史舞台。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










