shallowRef 仅对 .value 引用本身响应式,不对内部属性代理;其 .value 是普通对象,赋值不触发依赖更新,仅当 .value 被重新赋值时才触发响应。

shallowRef 的“浅层代理”不是在对象上做了一层浅的 Proxy,而是根本没对 .value 内部做任何 Proxy。它只把 .value 这个引用本身接入 Vue 的响应式系统,内部数据保持原样——仍是普通对象、普通数组,没有被 reactive 处理过。
它和 ref 的底层行为完全不同
ref({ a: { b: 1 } }) 会立刻对整个对象树递归调用 reactive(),给 a、a.b 都加上 Proxy,每次读写都触发 track/trigger;
shallowRef({ a: { b: 1 } }) 则让 .value 直接指向这个原始对象,Vue 完全不碰 a 或 b,只监听 “.value 这个指针有没有换人”。
你可以这样验证它的“无代理”特性
- 打印
shallowRef({ x: 1 }).value,得到的是 plain object,console.log(toRaw(obj.value) === obj.value)返回 true - 而
ref({ x: 1 }).value是一个 Proxy 实例,toRaw才能拿到原始对象 - 对 shallowRef.value.x = 2 赋值后,不会触发任何依赖收集或更新,因为 Vue 根本没为 x 建立过依赖链
什么时候你会明显感觉到“只盯 .value”
- 模板里写
{{ state.value.user.name }}:name 改了,页面不动;但state.value = newState,整行刷新 - 计算属性里访问
computed(() => state.value.items.length):items 数组 push 新项,长度不重算;只有 state.value 换了对象才重新求值 - 用
Object.is(oldValue, newValue)判断时,shallowRef 只比 .value 引用是否变化,不深比较内部
它不是“限制深度”,而是“跳过深度处理”
别把它理解成“代理只到第一层”。它压根没启动代理流程——就像寄快递,ref 是把整箱货拆开每件贴追踪码;shallowRef 是只给箱子贴一张单号,里面装啥、怎么动,快递公司不管。


















