“影子对象”是 reactive 返回的 Proxy 代理实例,它不复制数据,而是通过 Proxy+Reflect 动态转发读写操作,并在 get/set 中执行 track/trigger 完成依赖收集与更新通知;其内部依托 WeakMap→Map→Set 三层映射结构实现响应式联动。

“影子对象”不是 Vue 官方术语,而是开发者对 reactive 返回的 Proxy 实例的一种形象说法——它像原对象的“影子”,外观行为几乎一致,但所有读写操作都会被拦截、追踪和响应。这个代理对象本身不存储数据,也不复制属性,而是通过 Proxy + Reflect 动态转发访问,同时在背后完成依赖收集与更新通知。
影子对象的本质是 Proxy 代理,不是副本
调用 reactive({ count: 0 }) 后,返回的不是一个深拷贝或新对象,而是一个由 new Proxy(target, handler) 创建的代理实例。它和原始对象内存地址不同,=== 判断为 false,但访问 proxy.count 或赋值 proxy.count = 1 的效果与直接操作原对象完全一致。
- 原始对象仍存在,且不应再被直接使用(Vue 文档明确建议“只用代理,不用原始”)
- 代理对象内部不保存属性值,每次
get都通过Reflect.get去读原始对象;每次set都通过Reflect.set写回原始对象 - 代理仅添加了拦截逻辑:读时调用
track()收集依赖,写时调用trigger()触发更新
内部结构包含三层关键映射关系
Vue 3 的响应式系统靠三个嵌套的数据结构协同工作,共同支撑这个“影子”的智能行为:
-
TargetMap:
WeakMap,以原始对象为键,缓存它的所有响应式状态 -
DepsMap:
Map,记录该对象每个属性(如'count')对应的依赖集合 -
Dep:
Set,存放所有依赖这个属性的副作用函数(比如组件的 render 函数)
当 proxy.count 第一次被模板读取时,get 拦截器会从这三层结构中定位到 count 对应的 Dep,并把当前 effect 加进去;修改时则遍历该 Dep 执行所有 effect。
构建 Shopify 嵌入式应用(Embedded App)的开发技能:基于 @shopify/shopify-app-react-router + React Router v7 + Admin API 2026-04。覆盖 shopifyApp 配置、authenticate.admin 的 GraphQL/REST 调用、Webhook 校验、Session 存储、订阅计费、App Bridge 导航、部署,并新增:适用场景判断、安全最佳实践、错误处理与容错、最佳实践、常见问题 FAQ、高级场景。G
为什么需要 WeakMap 缓存代理?
为了避免重复代理同一对象(造成内存浪费和行为不一致),Vue 内部用 WeakMap 把原始对象和它的代理实例关联起来:
- 同一对象多次传给
reactive,始终返回同一个代理实例 -
WeakMap的弱引用特性可避免内存泄漏:当原始对象不可达时,对应缓存自动被垃圾回收 - 这也是为什么你不能对已代理对象再次 reactive —— 它会直接返回已有代理,而不是新建一层
影子对象的“深层响应”如何实现?
reactive 的响应是惰性且递归的:只有访问到某个嵌套属性时,才会对它的值(如果是对象)创建新的代理。
- 例如
reactive({ user: { profile: { name: 'A' } } }) - 初始只代理外层对象,
proxy.user返回的是原始{ profile: {...} }(但已被标记为“待代理”) - 当你读取
proxy.user.profile时,触发get拦截,检测到profile是对象,才调用reactive(profile)创建下一层代理 - 这种按需代理显著降低初始化开销,也避免代理不需要响应的静态数据










