解构 reactive 对象会丢失响应性,因为解构获取的是原始值或普通对象引用,不再经过 proxy 的 get/set 拦截,导致依赖收集和更新通知失效。

因为 reactive 返回的是一个 Proxy 实例,而解构操作会脱离 Proxy 的 get/set 拦截路径,导致依赖收集和更新通知机制完全失效。
Proxy 只代理顶层对象,不代理“被取出来”的值
reactive() 内部调用 createReactiveObject,最终返回一个 new Proxy(target, mutableHandlers)。这个 Proxy 仅拦截对 该代理对象本身 的属性访问(如 state.count),不拦截你从它里面“拿出来”的任何东西。
例如:
-
const state = reactive({ user: { name: '张三' } })→ state 是代理,state.user 也是代理(因 get 中递归调用了 reactive) -
const { user } = state→ 这行代码触发一次 get(state, 'user'),拿到的是 原始 user 对象的引用,不是它的代理副本 - 后续对 user.name 的读写,走的是普通对象路径,不再经过任何拦截器
解构本质是“一次读取 + 普通变量赋值”
JavaScript 解构没有响应式语义,它只是语法糖:
- 对基本类型(string/number/boolean):解构得到的是值拷贝,彻底脱离响应系统
- 对嵌套对象:解构得到的是内存地址引用,但该引用指向的不再是 Proxy,而是原始目标对象
- 对深层属性(如 const { name } = state.user):name 直接是字符串字面量,连对象引用都没有,完全扁平化出响应链
get/set 拦截器只在“通过代理访问”时触发
Vue 的 track(依赖收集)和 trigger(更新通知)都发生在 Proxy 的 get/set 内部:
- ✅
state.count++→ 走 set 拦截 → 触发 trigger → 更新视图 - ❌
const { count } = state; count++→ 修改的是 let 变量 count,不触碰 state,更不进 set 拦截 → 视图静默
源码关键逻辑印证
查看 mutableHandlers.get 实现:
- 第一件事是
track(target, key)—— 把当前副作用(如组件 render)记为 target.key 的依赖 - 然后
Reflect.get(target, key, receiver)获取值 - 若结果是对象,会
return reactive(res)做递归代理 - 但注意:这个
reactive(res)返回的新代理,只在 下一次通过 state.user 访问时才生效;解构后你跳过了这层访问,自然拿不到代理











