📚前端面试题速记
Q16

Vue 3 中的 shallowReactive 和 shallowRef 有何用途?

速记答案(30 秒)

两者都只对"第一层"做响应式,内部嵌套对象原样保留为非响应式。适用场景:大对象/大数组、第三方实例等,减轻性能和行为上的风险。

📖 详细讲解

标准面试回答(推荐记住)

shallowReactive 只代理对象的第一层属性,嵌套对象不会被转为响应式;shallowRef 同理,只追踪 .value 的变化。适用于大型对象、第三方库实例等不需要或不能深度代理的场景,可以避免不必要的性能开销。

性能优化:Shallow API

默认的 reactiveref 是深度的。当有一个巨大的嵌套对象(例如:图表库的大型配置对象、从服务端获取的只读大列表)时,深度递归代理会带来巨大的性能开销。

shallowReactive:

  • 只有根级别的属性是响应式的。
  • 读取嵌套属性不会触发 getter 代理,不会递归转为 Proxy。
  • 赋值嵌套属性不会触发 setter 更新。

shallowRef:

  • 只有 .value 的再次赋值是响应式的。
  • 修改 .value 内部对象的属性不会触发更新。
  • 常用于 shallowRef(someBigObject),当需要更新时,直接替换整个对象 ref.value = newBigObject,或者配合 triggerRef(ref) 手动触发。

场景:

  • 集成第三方库(ECharts 实例、Three.js 对象)
  • 不可变数据结构
  • 防抖/节流的中间状态

面试要点

  • 理解浅层响应式的含义
  • 知道适用场景
  • 了解 triggerRef 的用途

💻 代码示例

shallow 系列用法
import { shallowReactive, shallowRef, triggerRef } from 'vue'

// shallowReactive - 只代理第一层
const state = shallowReactive({
  count: 0,
  nested: { value: 1 }  // 不是响应式的!
})

state.count++ // ✅ 触发更新
state.nested.value++ // ❌ 不会触发更新
state.nested = { value: 2 } // ✅ 触发更新

// shallowRef - 只追踪 .value 的替换
const data = shallowRef({ items: [] })

data.value.items.push(1) // ❌ 不会触发更新
data.value = { items: [1] } // ✅ 触发更新

// 强制触发更新
triggerRef(data)