用 shallowreactive 替代 reactive 是优化大型只读对象响应式开销最直接有效的方式——它只代理对象第一层属性,跳过深层 proxy 递归和依赖追踪,避免内存占用高、初始化慢、gc 压力大等问题。

用 shallowReactive 替代 reactive,是优化大型只读对象响应式开销最直接有效的方式——它跳过深层代理,只让对象第一层属性保持响应性,避免无谓的 Proxy 递归和依赖追踪。
为什么 reactive 在大型对象上会变慢
reactive 会对传入对象的所有嵌套层级(包括数组、对象、Map、Set 等)递归创建 Proxy。比如一个含 1000 条用户数据的列表,每条用户又有 profile、address、orders 等多层嵌套结构,reactive 就会为每一层字段都建立 getter/setter 拦截器,并注册依赖。即使你只读取 list[0].name,整个树状结构都已被深度代理,内存占用高、初始化慢、GC 压力大。
shallowReactive 的作用边界很明确
它只代理对象自身属性的读写,不深入任何嵌套值。也就是说:
-
state.list改变(如赋新数组)→ 触发更新 -
state.meta.total改变 → 不触发更新(因为meta是普通对象,未被代理) -
state.list.push(...)→ 不触发更新(数组本身没被 proxy,无法拦截方法调用)
典型适用场景与写法
适用于后端返回的、仅用于展示的“静态”数据结构,例如分页表格响应、配置项快照、日志详情等。
示例:
✅ 正确用法(只读 + 浅层更新)import { shallowReactive } from 'vue'
const tableData = shallowReactive({
list: [], // 后续可整体替换:tableData.list = newData
pagination: { // 内部属性修改不会响应,但没关系——你本来就不改它
page: 1,
size: 20
},
loading: false // 这个布尔值需要响应,所以放外层
})
// ✅ 安全更新(触发视图刷新)
tableData.list = response.data.items
tableData.loading = false
// ❌ 不要这样做(内部修改无效且无提示)
tableData.pagination.page = 2 // 无响应,页面不更新
配合其他 API 提升实用性
shallowReactive 单独用略显受限,常搭配以下方式增强可控性:
- 对需要响应的深层字段,单独用
ref或shallowRef包裹,例如:selectedId = ref(null) - 用
toRefs解构浅响应对象时仍有效,但注意解构出的仍是普通值(除非原属性本身就是 ref) - 若需监听某深层字段变化,改用
watch(() => data.nested.field)显式取值,而非依赖响应式穿透










