shallowRef 是 Vue 3 自发布起内置的浅层响应式 API,仅对 .value 本身变化响应,不递归代理内部属性,适用于大型数据整体替换场景(如万级表格、ECharts 实例、静态配置),可降渲染开销 60%–70%,配合 triggerRef 支持局部修改后手动触发更新。

shallowRef 不是 Vue 3 “新出”的 API,它从 Vue 3 发布起就已内置,但长期被低估。它的核心作用很明确:**避免对大型数据结构做无意义的深度响应式代理,从而显著降低内存占用和渲染开销**。尤其在处理上万行表格、复杂图表实例或静态配置数据时,效果非常直接——实测可减少 60%–70% 的渲染时间。
适合用 shallowRef 的典型场景
当你确认数据只会在整体替换时需要触发更新,而内部字段极少甚至从不单独修改,shallowRef 就是最自然的选择:
-
超大表格数据源:比如 Element Plus 的
el-table绑定一个含 10,000 条记录的数组。用ref会让每条记录、每个嵌套字段都被递归转为响应式,创建大量 Proxy 实例;改用shallowRef后,仅监听.value是否被整体赋值,初始化快、内存省、GC 压力小 -
第三方库实例(如 ECharts、Three.js):这些对象自身有完整状态管理,Vue 深度代理反而干扰其生命周期,甚至引发报错。shallowRef 让实例保持原始形态,只在你主动替换整个实例(例如
chart.value = initChart())时更新视图 -
静态字典或只读配置数据:比如城市列表、国家编码表、API 返回的原始 JSON 数组。它们本身不需响应式变更,却因误用
ref被层层代理,白白消耗资源
怎么用?关键在“怎么更新”
声明方式和 ref 几乎一样,但行为逻辑完全不同。重点不是“怎么写”,而是“怎么改”:
Vue 3 组合式 API 与 最佳实践,涵盖代码组织、useXxx 模式、组合式函数设计、Store 集成、响应式优化及功能抽取。
-
声明:
const tableData = shallowRef([{ id: 1, name: '张三' }]) -
整体替换才触发更新:
tableData.value = newDataList✅(适用于分页、筛选、刷新等操作) -
修改内部属性默认不更新:
tableData.value[0].status = 'done'❌(数据确实变了,但界面不会重绘) -
需要局部修改 + 触发更新?用
triggerRef:tableData.value[0].status = 'done'; triggerRef(tableData)✅(批量改完再统一触发,比逐条更新更高效)
shallowRef 和 shallowReactive 的区别
两者都是“浅层”,但包装形态和适用习惯不同:
立即学习“前端免费学习笔记(深入)”;
-
shallowRef 返回带
.value的 ref 对象,适合你习惯用.value赋值、且可能混用基本类型与对象的场景(例如shallowRef(0)或shallowRef({})都合法) -
shallowReactive 直接返回响应式对象,没有
.value,适合纯对象结构、希望像普通对象一样访问属性(如state.users.length)的写法 -
共同点:都只响应顶层变化;都不自动代理深层嵌套;都需要
triggerRef或整体替换来驱动更新
容易踩的坑
shallowRef 用错地方,反而会让逻辑更难维护:
- 不要把它用于频繁修改内部字段的业务对象(比如用户编辑表单),这种场景更适合
ref或reactive - 不要嵌套在深层响应式对象中(例如
reactive({ data: shallowRef(...) })),会导致响应行为不一致,调试困难 - 搭配
markRaw使用更稳妥:如果数据里包含 DOM 元素、第三方类实例等本就不该被响应式接管的内容,先用markRaw标记,再包裹进shallowRef


















