Proxy可监听新增/删除属性、数组索引赋值等,而Object.defineProperty不能;但IE不支持Proxy,Vue2为兼容沿用defineProperty,Vue3/Solid默认用Proxy。

为什么直接用 Proxy 而不是 Object.defineProperty
因为 Object.defineProperty 无法监听新增/删除属性、数组索引赋值、length 变更,而 Proxy 的 set/get/deleteProperty/ownKeys 等 trap 能覆盖这些场景。但要注意:IE 完全不支持 Proxy,且 Vue 2 用 defineProperty 是因需兼容 Vue 1 的响应式语义;现代实现(如 Vue 3、Solid)默认走 Proxy。
Proxy 响应式核心:拦截 get 收集依赖,set 触发更新
关键不在代理本身,而在「谁读了这个值」和「谁该被通知更新」的关联逻辑。需要一个全局依赖收集器(通常叫 activeEffect),在 get 中把当前执行的副作用函数存进目标对象+属性名对应的依赖集合里;在 set 后遍历该集合并执行所有函数。
- 必须用
WeakMap存对象级依赖映射:const depsMap = new WeakMap(),避免内存泄漏 -
gettrap 中要判断是否处于依赖收集阶段(即activeEffect存在),才调用track(target, key) -
set返回true才算赋值成功,否则会静默失败;记得return Reflect.set(...) - 对数组方法如
push、pop,需单独处理:它们可能触发length或索引变更,得在set和get外加ownKeys、has拦截
如何处理嵌套对象与数组的响应式转换
不能只代理第一层——用户访问 obj.a.b.c 时,a 和 b 也得是响应式对象。所以 get 返回值如果是对象或数组,要递归调用 reactive() 包装(但需缓存,避免重复代理同一对象)。
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
- 用
Map缓存已代理对象:const proxyMap = new Map(),键为原始对象,值为对应Proxy - 数组需特殊处理:
Array.isArray(value)时,返回经reactive包装的新数组,而非原生数组(否则arr[0] = x不触发set) - 避免代理非对象类型(
null、undefined、原始值),直接返回原值 -
reactive函数应返回Proxy,而不是修改原对象——这是不可变设计的关键
常见踩坑点:for...in、Object.keys()、解构、JSON.stringify()
这些操作不经过 get,所以不会触发依赖收集,也不受响应式影响。例如 const { count } = state 解构后,count 就脱离响应式链了;JSON.stringify(state) 会卡死(无限递归代理对象)。
- 解决
for...in/Object.keys():在ownKeystrap 中返回Reflect.ownKeys(target)并调用track(target, 'keys'),让遍历也成依赖 - 解构赋值必须配合
toRefs()工具函数,它对每个属性返回一个带get/set的ref对象 -
JSON.stringify()崩溃是因为默认序列化会读取所有属性,触发无限代理;应在get中对toJSON、toString等特殊键直接返回原始值,或重写toJSON方法 - 注意
Proxy无法代理class实例的方法调用,若需监听方法执行,得靠装饰器或手动包装
真正难的不是写通 Proxy,而是边界 case 的收敛——比如 Map/Set 的响应式、Symbol 键、in 操作符、instanceof 判断。这些要么得扩展 trap,要么得放弃代理改用封装 API。

















