在Vue3中,ref和shallowRef的选择取决于数据的结构和性能需求。以下是详细的选择指南:

1. 基础类型(String, Number, Boolean等)

✅ 优先使用 ref

// 基础类型 - 使用 ref(两者无性能差异)
const count = ref(0)
const name = ref('张三')
const isLoading = ref(false)
1
2
3
4

理由:对于基础类型,ref和shallowRef的行为完全一致,没有性能差异,使用ref更统一。

2. 单层对象(平坦对象)

✅ 推荐使用 ref

// 单层对象 - 使用 ref
const user = ref({
  name: '张三',
  age: 25,
  email: 'zhangsan@example.com'
})
// 修改属性会自动触发响应式更新
user.value.name = '李四'
1
2
3
4
5
6
7
8

理由:

  • 对象属性会被自动深度响应化
  • 代码更直观,符合预期
  • 性能开销可接受(只有一层深度)

3. 数组对象

🔄 根据需求选择

场景A:数组元素需要响应式更新
// 需要响应式更新数组元素 - 使用 ref
const list = ref([
  { id: 1, name: '项目1' },
  { id: 2, name: '项目2' }
])

// 元素修改会触发更新
list.value[0].name = '新项目1'  // ✅ 触发更新
1
2
3
4
5
6
7
8
场景B:只关心数组本身的变化
// 只关心数组整体替换 - 使用 shallowRef
const largeList = shallowRef([
  // 大型数据集,元素不需要响应式
  { id: 1, data: { /* 复杂嵌套数据 */ } },
  // ... 很多数据
])

// 整体替换会触发更新
largeList.value = newList  // ✅ 触发更新

// 元素修改不会触发更新(可能带来性能提升)
largeList.value[0].data.value = 123  // ❌ 不会触发更新
1
2
3
4
5
6
7
8
9
10
11
12

4. 多层嵌套对象

⚖️ 根据性能需求权衡

场景A:需要完整的响应式
// 多层对象需要深度响应式 - 使用 ref
const complexData = ref({
  user: {
    profile: {
      name: '张三',
      address: {
        city: '北京',
        street: '...'
      }
    },
    settings: {
      // 嵌套设置
    }
  }
})

// 任何深层的修改都会触发更新
complexData.value.user.profile.address.city = '上海'  // ✅ 触发更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
场景B:性能优先,减少响应式开销
// 多层对象 - 使用 shallowRef 优化性能
const heavyData = shallowRef({
  config: { /* 大型配置对象,很少修改 */ },
  metadata: { /* 元数据 */ },
  // 深层嵌套但不常修改的数据
})

// 只有顶级属性赋值会触发更新
heavyData.value = newData  // ✅ 触发更新

// 深层修改需要手动处理
function updateDeepProperty() {
  heavyData.value.config.detail.value = 'new'
  triggerRef(heavyData)  // 手动触发更新
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

5. 特殊场景

shallowRef 的优势场景

大型数据集性能优化
// 大数据列表,性能敏感
const bigData = shallowRef({
  items: Array(10000).fill().map((_, i) => ({
    id: i,
    // 每个元素都有复杂嵌套
    data: { /* ... */ }
  }))
})
1
2
3
4
5
6
7
8
第三方库对象
// 第三方库实例,不需要Vue响应式
const externalLib = shallowRef(new ExternalLibrary())

// 外部对象,避免不必要的响应式代理
const domElement = shallowRef(document.getElementById('app'))
1
2
3
4
5
频繁修改的深对象
// 频繁修改但不需要每次触发更新
const cache = shallowRef({
  // 缓存数据,批量更新
})

// 批量修改后一次性触发
function batchUpdate() {
  // ... 多次修改
  triggerRef(cache)
}
1
2
3
4
5
6
7
8
9
10
选择总结表格
数据类型 推荐选择 理由
基础类型 ref 两者无差异,统一使用ref
单层对象 ref 代码直观,性能可接受
数组(需元素响应式) ref 元素修改需要响应式
数组(只替换整体) shallowRef 性能优化
多层对象(需深度响应) ref 完整响应式支持
多层对象(性能敏感) shallowRef 减少响应式开销
第三方对象/DOM shallowRef 避免不必要的代理
最佳实践建议
  1. 有序列表默认使用ref,除非有明确的性能需求
  2. 对于频繁更新的大数据或深层嵌套对象,考虑shallowRef
  3. 使用shallowRef时,配合triggerRef()手动控制更新时机
  4. 监控性能时,如果发现响应式开销过大,考虑将部分数据改为shallowRef
// 混合使用示例
const state = {
  // 深度响应式数据
  user: ref({ profile: { /* ... */ } }),
  
  // 浅响应式大数据
  largeList: shallowRef([/* ... */]),
  
  // 基础数据
  count: ref(0)
}
1
2
3
4
5
6
7
8
9
10
11

记住:过早优化是万恶之源。先使用ref实现功能,遇到性能瓶颈时再考虑shallowRef优化。