在 Vue 中,refshallowRef 都是用于创建响应式引用的 API,但它们的响应式深度和适用场景有显著区别。以下是两者的详细对比:

1. 核心区别:响应式深度

  • ref深层响应式
    对值进行递归监听,无论是基本类型还是对象/数组,修改其内部属性(如对象的属性、数组的元素)都会触发响应式更新。
  • shallowRef浅层响应式
    仅监听 .value直接赋值,不追踪对象/数组内部属性的变化,修改内部属性不会触发更新。

2. 使用示例对比

场景1:处理基本类型(数字、字符串等)

两者表现一致,因为基本类型没有内部属性:

// ref
const countRef = ref(0)
countRef.value++ // 触发更新// shallowRef
const countShallowRef = shallowRef(0)
countShallowRef.value++ // 触发更新(与ref一致)
场景2:处理对象/数组

两者行为差异明显:

// ref(深层响应式)
const objRef = ref({ count: 0 })
objRef.value.count++ // 触发更新(内部属性变化被监听)// shallowRef(浅层响应式)
const objShallowRef = shallowRef({ count: 0 })
objShallowRef.value.count++ // 不触发更新(内部属性变化不被监听)
objShallowRef.value = { count: 1 } // 触发更新(直接替换.value)

3. 手动触发更新的差异

  • shallowRef 可以通过 triggerRef 强制触发更新(即使只修改了内部属性):
const data = shallowRef({ count: 0 })
data.value.count++ // 不触发更新
triggerRef(data) // 手动触发更新,视图刷新
  • ref 无需手动触发,内部属性变化会自动更新。

4. 性能与适用场景

特性

ref

shallowRef

响应式深度

深层(递归监听)

浅层(仅监听 .value 赋值)

性能开销

较高(递归追踪所有属性)

较低(仅追踪顶层)

适用场景

需监听内部属性变化的对象/数组

大型数据、无需深层监听的场景

典型用例

表单数据、业务对象

图表数据、大型列表、静态数据

5. 总结:如何选择?

  • 优先用 ref
    大多数场景下(尤其是需要监听对象/数组内部变化时),ref 的自动深层响应式更符合直觉,无需手动处理更新。
  • shallowRef 优化性能
    当处理大型数据(如包含 thousands 条记录的列表、复杂图表配置)或无需追踪内部变化的数据时,shallowRef 能减少响应式系统的开销,提升性能。
  • 注意
    shallowRef 需要手动管理内部属性的更新(通过 triggerRef),过度使用可能导致逻辑复杂,建议仅在明确有性能问题时使用。