Vue 3 的深层监听通过 Proxy 懒递归代理实现:首次访问嵌套属性时,get 拦截中检测值为对象/数组即调用 reactive 递归包装,逐层创建 Proxy;基础类型和内置对象不代理,shallow 系列 API 显式终止递归。

Vue 3 的“深监听”不是靠手动配置触发的,而是响应式系统在初始化时就自动完成的递归代理行为。它不依赖 deep: true 这类开关,本质是 Proxy 对嵌套结构的逐层拦截与再代理。
递归代理是怎么发生的?
当你调用 reactive({ a: { b: { c: 1 } } }),Vue 并不会只给顶层对象加一层 Proxy。它在 get 拦截中判断:如果读取的值是对象(或数组),就立刻用 reactive() 递归包装——也就是对 b 创建新 Proxy,再对 c 所在的对象继续处理。这个过程是懒执行、按需递进的,不是一次性全量展开。
- 首次访问
obj.a→ 触发 get → 发现a是对象 → 自动调用reactive(a) - 后续访问
obj.a.b→ 同样触发 get → 再次递归代理b - 修改
obj.a.b.c = 2→ 触发 set → 通知依赖更新,且不影响未访问过的分支
为什么 ref 也支持深层响应?
ref 看似包装一层 { value: ... },但它内部仍基于 reactive 实现。例如 ref({ x: { y: 1 } }),其 .value 属性实际指向一个 reactive 对象,所以 .value.x.y 的变化天然可追踪。
Vue 3 组合式 API 与 最佳实践,涵盖代码组织、useXxx 模式、组合式函数设计、Store 集成、响应式优化及功能抽取。
-
ref(10)→ 内部是reactive({ value: 10 }) -
ref({ a: { b: 1 } })→ 内部是reactive({ value: reactive({ a: reactive({ b: 1 }) }) }) - 所有嵌套层级都由 Proxy 逐层接管,无需额外标记
什么时候递归会“停”?
递归代理不是无条件无限深入。Vue 会对基础类型(string/number/boolean/null/undefined/Symbol)、内置对象(Date、RegExp、Map、Set 等)跳过代理,只做浅层响应。同时,shallow 系列 API(如 shallowRef、shallowReactive)会显式切断递归链:
立即学习“前端免费学习笔记(深入)”;
-
shallowReactive({ a: { b: 1 } })→ 只代理顶层,a本身仍是普通对象,修改a.b不触发更新 -
shallowRef({ x: 1 }).value = { y: 2 }→ 仅触发.value引用变更,不递归代理新对象 - 若需更新视图,得手动调用
triggerRef()(仅对 shallowRef 有效)
和 Vue 2 的深度监听有本质区别
Vue 2 的 deep: true 是 watch 的运行时选项,它通过遍历+递归取值模拟“深度”,但无法响应新增属性或数组索引赋值;而 Vue 3 的递归代理是数据创建时就确立的响应关系,覆盖所有合法操作:
- 支持
arr[0] = 'new'、arr.length = 0、obj.newKey = 'val' - 无需
Vue.set或特殊 API 补丁 - 数组方法(push/pop/shift/unshift/splice)全部原生响应

















