shallowref比ref更快的前提是不依赖内部属性变化触发更新;ref递归代理整个对象树,shallowref仅响应.value引用变化,适用于大数据列表、只读配置和外部状态集成等场景。

是的,shallowRef 在多数涉及大型或深层对象的场景下,确实比 ref 更快——但这个“更快”有明确前提:它只在你不依赖内部属性变化触发更新时才成立。
关键区别不在“谁更快”,而在“追踪什么”
ref 对象会递归调用 reactive(),把整个对象树(包括所有嵌套属性、数组元素、Map/Set 等)都转为响应式代理,每访问一个深层属性,Vue 都要执行 track 操作;shallowRef 则跳过这一步,只对 .value 这个引用本身做响应式处理。
- ref:每次读取
user.value.profile.avatar.url,都会触发多层 track(profile → avatar → url) - shallowRef:读取同一路径时,只 track
user.value这一层,内部路径完全绕过响应式系统
性能优势真正体现的三个典型场景
不是所有对象都适合 shallowRef,它的提速集中在以下情况:
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
-
大数据列表的行数据:如表格中 1000 条记录,每条是深度嵌套对象。用 ref 会让 Vue 建立上千个深层依赖;用 shallowRef 后,仅当整行被替换(
rows[i] = newRow)才触发更新,筛选/排序操作不再引发大量依赖通知 - 配置类只读对象:比如从后端拉取的 UI schema、表单规则、国际化词条。这些数据运行时几乎不改内部字段,只在初始化或刷新时整体替换——正好匹配 shallowRef 的设计逻辑
- 与外部状态库集成:如配合 Zustand、Jotai 或原生 localStorage 数据。这些数据本就不走 Vue 响应式链路,用 shallowRef 包一层,避免无意义的 reactive 转换开销
容易被忽略的代价和补救方式
shallowRef 不是“免费加速”,它把响应式责任转移给了开发者:
- 修改
obj.value.name不触发更新 → 必须手动调用triggerRef(obj) - 若需部分属性响应式(如只让
obj.value.status可响应),不能靠 shallowRef 单独解决,得组合reactive({ status: ... })+shallowRef或用computed显式导出 - 基本类型(数字、字符串)用 shallowRef 和 ref 性能几乎无差别,此时选 ref 更符合直觉,无需额外记忆规则
怎么判断该不该换?看这两点
不用猜,直接观察:
- 打开 Vue Devtools → Performance 标签 → 触发一次数据操作,看 “Reactive effect trigger” 数量是否远超实际需要更新的组件数
- 在 setup 中打印
JSON.stringify(Object.keys(reactiveObj)),如果返回结果包含大量深层 key(如["a", "b", "c", "d.e.f.g", "h.i.j.k.l"]),说明 ref 正在做冗余追踪










