shallowRef仅响应.value顶层赋值,内部属性修改不触发更新;ref对.value内引用类型递归调用reactive实现深层响应。

shallowRef 和 ref 在嵌套对象上的响应式行为差异,核心在于“解包层级”和“代理深度”——不是能不能响应,而是“在哪一层触发更新”。
shallowRef:只盯 .value 本身,不进内部
shallowRef({ a: 1, b: { c: 2 } }) 创建的是一个浅层 ref:
- 修改 shallowRef.value = { a: 2 } → 触发响应(顶层赋值)
- 修改 shallowRef.value.b.c = 3 → 完全静默(内部属性变更不被追踪)
- 访问 shallowRef.value.b.c 读得到值,但改它不会通知视图重绘
- 它内部的 { c: 2 } 是普通对象,没被 reactive 包裹,也没 Proxy 代理
ref:默认深层,内部也代理
ref({ a: 1, b: { c: 2 } }) 创建的是一个响应式 ref,其 .value 会被自动递归处理:
- 修改 ref.value.b.c = 3 → 触发更新(因为内部对象已被 reactive 包装)
- 新增或删除 ref.value.b.d = 4 或 delete ref.value.b.c → 同样响应
- 本质是:ref 对象的 .value 若为引用类型,Vue 内部会调用 reactive 转换它
模板中都自动解包,但逻辑起点不同
在组件模板里,两者都能直接写 {{ myRef.b.c }},看起来一样。但背后机制不同:
- ref 的解包是“深度穿透”的:Vue 知道它内部有响应式结构,所以能连着读到 b.c
- shallowRef 的解包只到 .value 这一层;后续属性访问(如 b.c)只是普通 JS 属性读取,不触发依赖收集
- 所以如果你 watch shallowRef,只监听 shallowRef.value 变化才有效;watch shallowRef.value.b.c 永远不会响应
什么时候选 shallowRef?
适合那些你明确知道“内部结构不会变”或“变了也不需要驱动 UI 更新”的场景:
- 大型静态配置对象(如图表 options),只初始化一次,后续只读
- 第三方库返回的复杂对象(如 Map、Canvas 实例),不需要 Vue 去 proxy 它们
- 性能敏感路径中,避免对深层嵌套对象做无谓的 Proxy 代理和依赖追踪


















