vue 3响应式基于proxy懒递归代理,首次访问时逐层创建响应式对象,ref底层也通过reactive实现深度响应,shallow系列api显式终止递归,watch与watcheffect依赖同一收集系统但触发逻辑不同。

Vue 3 的响应式监听不是靠“开启开关”实现的,而是从数据创建那一刻起,就由 Proxy 自动建立可追踪的嵌套结构。它不依赖 deep: true,也不需要手动标记,所有合法操作——包括新增属性、数组索引赋值、length 修改、Map/Set 操作——天然响应。
递归代理:深监听的真正来源
当你调用 reactive({ a: { b: { c: 1 } } }),Vue 并不会一次性把三层对象全转成 Proxy。它采用懒递归策略:
- 首次读取 obj.a → 触发 get 拦截 → 发现 a 是普通对象 → 立即用 reactive(a) 包装,生成新 Proxy
- 后续访问 obj.a.b → 同样触发 get → 再次对 b 执行 reactive
- 修改 obj.a.b.c = 2 → 触发 set → 通知依赖更新,且未访问过的分支(如 obj.x.y)不会被代理,节省开销
ref 也支持深层响应,因为它底层仍是 reactive
ref 看似只包一层 { value: ... },但它的内部实现决定了它同样具备深度能力:
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
-
ref(10) → 实际是
reactive({ value: 10 }),value 是响应式字段 -
ref({ x: { y: 1 } }) → 展开为
reactive({ value: reactive({ x: reactive({ y: 1 }) }) }) - 所有嵌套层级都由 Proxy 逐层接管,.value.x.y 的变化可直接被侦测
递归不是无限的:哪些情况会停止代理?
Vue 有明确规则避免无意义递归:
- 基础类型(string/number/boolean/null/undefined/Symbol)不代理,只做原始值响应
- 内置对象(Date、RegExp、Map、Set、Promise 等)跳过 Proxy,保留原行为
- shallowReactive 和 shallowRef 显式切断递归链:前者只代理顶层,后者仅保证 .value 引用变更可触发,新赋值的对象不会自动 reactive
- 若用 shallowRef 赋了新对象,需手动调用 triggerRef() 才能通知更新
watch 与 watchEffect 的监听逻辑差异
两者都基于同一套依赖收集系统,但触发方式不同:
- watch 接收显式 source(如 () => state.count),只追踪该表达式中实际读取的响应属性,支持新旧值对比和防抖等选项
- watchEffect 会立即执行一次传入函数,并自动收集其中所有响应式依赖,适合“副作用驱动”的场景,比如发起请求或更新 DOM
- 它们最终都调用同一个核心函数 doWatch,区别在于依赖收集的时机和范围
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










