当前位置: 首页 > news >正文

Vue2 的 $set 和 Vue3 的 Proxy:一个老前端踩了多年坑才想明白的事

用过 Vue2 好些年,你真的懂它为什么需要$set吗?Vue3 又为什么换成Proxy?这篇不用官方术语堆砌,就用我当年真实踩过的坑,把这俩的区别掰开揉碎讲清楚。

先说个丢人的事

前阵子面试,对方随口问了句:"为什么 Vue3 用 Proxy,不用 Vue2 那套Object.defineProperty了?"

我当场卡了大概半分钟。Vue2 我用了五年,Vue3 也上手做项目了,日常$set用得贼溜——但真被问到"为什么"的时候,发现自己只是会用,根本没真懂。

回来翻了翻源码和文档,才把这块理顺。今天借这个机会,把自己的理解记下来,也算是给当年那个卡壳的自己一个交代。

用包租婆查水电费,理解 Vue2 的响应式

先把 Vue2 的响应式想成包租婆管出租屋

租出去每一间房,包租婆都单独装了一块电表。哪间房用电了,对应那块表就转,包租婆就知道"这间房状态变了,去收电费"。

但问题来了——你要是新租出去一间房,却忘了装电表,这间房用再多电,包租婆也收不到信号。

Vue2 里这个"装电表"的动作,就是Object.defineProperty给每个属性加getter/setter。它在组件初始化时,把data已有的每个属性都"装好表"。所以:

  • 你一开始写在data里的字段,改了能触发更新,没问题;
  • 但你运行时新加的属性(比如后台返回的对象没带某个字段,你直接obj.newKey = 1),这块"新房间"没装表,改了也没人知道,视图不更新。

这就是当年$set存在的根本原因——它是用来给"新房间"补装电表的。

我当年被坑的两个瞬间

第一个:this.user.age = 18不更新。

做中后台那会儿,接口返回的用户对象没带age字段。我在mounted里直接this.user.age = 18,页面死活不动。查了半天才反应过来:这个age是运行时加的,Vue2 管不到。得用this.$set(this.user, 'age', 18)才行。

后来我学乖了,凡是"可能后端不给全"的对象,先Object.assign一个带默认值的模板,提前把字段都声明好,把坑填在前面。

第二个:数组下标赋值不生效。

有一次this.list[0] = newValue,视图没反应。我甚至一度怀疑是框架 bug(现在想想真丢人)。最后发现 Vue2 根本拦不住数组的下标赋值,得用splice或者$set

这俩坑看着像"我代码写错了",其实根子上是同一个——defineProperty的能力边界。

Vue2 这套方案,到底有哪几个硬伤

踩完坑再回头看,就通透了。Object.defineProperty有三个绕不开的问题:

  1. 必须预先遍历已知属性。新增、删除属性它收不到信号(就是上面"新房间没装表")。
  2. 数组拦截不到push/pop这些方法 Vue2 是重写的(算 hack),但下标赋值这个原生操作它拦不住。
  3. 深层对象要递归遍历。嵌套越深,初始化时递归成本越高,而且是一上来就全量铺开的。

Vue3 怎么把这些问题端了的

Vue3 换成了Proxy,等于把"每间房装电表"改成了整栋楼装一个中央总闸

不管你是新住户、还是把房间拆了重建(新增/删除属性、数组下标改值),只要有人进出,总闸统一拦截、统一通知。所以 Vue3 里普通赋值就行,没有$set这层心智负担。

Proxy代理的是整个对象get/set/deleteProperty/has全都能拦,而且是惰性的——你访问到哪一层,它才代理哪一层,不用一开始全量遍历。正好把上面三个硬伤一一化解。

两段代码,对比着看更直观

js

// Vue2:给每个属性单独"装电表" function defineReactive(obj, key, val) { Object.defineProperty(obj, key, { get() { dep.depend() // 收集依赖 return val }, set(newVal) { val = newVal dep.notify() // 通知更新 } }) }

js

// Vue3:整栋楼交给一个 Proxy 总闸 const reactive = (obj) => new Proxy(obj, { get(target, key, receiver) { track(target, key) // 收集依赖 const res = Reflect.get(target, key, receiver) return typeof res === 'object' ? reactive(res) : res // 惰性代理 }, set(target, key, value, receiver) { Reflect.set(target, key, value, receiver) trigger(target, key) // 通知更新 return true } })

一句话总结差异:Vue2 是逐属性上锁,Vue3 是整栋楼门禁。

顺带提一句 React——它那边不玩"拦截对象"这套,是让你返回新状态(setState/ 不可变更新)来触发重渲染,完全是另一条路子,这里就不展开了。

给后来者的几句大实话

我熬了几年才明白:会用$set不等于懂响应式。真正懂,是能回答"它为什么存在""换 Proxy 解决了什么"。

所以如果你也在用 Vue,别像我一样只停在"能跑就行"。花一个下午把definePropertyProxy这两段最朴素的实现读一遍,比你刷十个面试题都值。

你们当年有没有被 Vue2 的响应式坑过?评论区聊聊,让我知道自己不是一个人在战斗。

http://www.jsqmd.com/news/1306504/

相关文章:

  • 3分钟快速上手:微信公众号爬虫完整指南与实战教程
  • 2026随州全域外墙漏水维修|筑宅安全域上门勘查施工 - 筑宅安
  • SIGABRT 进程主动终止故障模式详解
  • MATLAB bwconncomp连通分量分析:从算法原理到工程实践
  • UE4中移植ShaderToy镜头光晕:从GLSL到HLSL的完整实战指南
  • 分子胶药物:从不可成药靶点到SHP2降解的机制与应用
  • 文件能够导入,为什么仍然不代表光学模型已经打通?
  • 如何让Switch焕发新生:大气层整合包系统完全指南
  • 抖音直播数据采集技术深度解析:WebSocket协议与加密签名逆向工程实践
  • 如何通过5个步骤让Windows任务栏实现完美透明化:TranslucentTB完全指南
  • 终极指南:如何快速下载B站高清视频的免费高效方案
  • 深入解析Java日志基石:LoggerFactory.getLogger原理、最佳实践与性能优化
  • Windows平台终极指南:如何5分钟搭建专业级流媒体服务器SRS
  • WarcraftHelper:魔兽争霸3终极性能优化指南,三步解锁高帧率宽屏体验
  • 如何快速部署大麦网抢票神器:面向初学者的完整自动化工具指南
  • SpringBoot+Vue家教管理系统架构设计与实践
  • 人工智能深度学习系统班(第十五期),人工智能深度学习系统班第十四,十三期
  • Vue 3响应式进阶:shallowRef与triggerRef性能优化
  • 从原理到代码实现:深入理解DES对称加密算法
  • Seeeduino V2.2硬件解析与实战:经典Arduino兼容板的核心价值与应用
  • Java字节数组深度解析:从声明差异到内存管理与编码转换实战
  • UE5模型导入利器:Assimp插件原理、安装与实战指南
  • 如何快速搭建微信公众号爬虫:面向初学者的完整指南
  • 避坑指南:天津北辰跨省长途转运救护车车辆租赁,8月术后平稳转院全程陪护 - 甄选测评馆
  • ESP32-H2开发板实战:从低功耗物联网到Matter智能家居应用
  • SCI论文写作必备:从引言到审稿回复的全流程句式库与实战技巧
  • 选错geo优化公司哪家强到底有多坑:从技术底座到落地效果的全景对比
  • 2026年AI领域可贴牌OEM企业TOP10|合规靠谱厂商全维度盘点
  • 百考通:AI赋能论文降重,助力每一份研究从良好开端走向卓越成果,让你少走弯路
  • 2026西宁下水道疏通维修靠谱机构榜单 马桶地漏积水反臭倒灌彻底解决攻略 - 宅安选房屋修缮