在Vue3中,ref和shallowRef的选择取决于数据的结构和性能需求。以下是详细的选择指南:
1. 基础类型(String, Number, Boolean等)
✅ 优先使用 ref
// 基础类型 - 使用 ref(两者无性能差异)
const count = ref(0)
const name = ref('张三')
const isLoading = ref(false)
1
2
3
4
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
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
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
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
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
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
2
3
4
5
6
7
8
第三方库对象
// 第三方库实例,不需要Vue响应式
const externalLib = shallowRef(new ExternalLibrary())
// 外部对象,避免不必要的响应式代理
const domElement = shallowRef(document.getElementById('app'))
1
2
3
4
5
2
3
4
5
频繁修改的深对象
// 频繁修改但不需要每次触发更新
const cache = shallowRef({
// 缓存数据,批量更新
})
// 批量修改后一次性触发
function batchUpdate() {
// ... 多次修改
triggerRef(cache)
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
选择总结表格
| 数据类型 | 推荐选择 | 理由 |
|---|---|---|
| 基础类型 | ref | 两者无差异,统一使用ref |
| 单层对象 | ref | 代码直观,性能可接受 |
| 数组(需元素响应式) | ref | 元素修改需要响应式 |
| 数组(只替换整体) | shallowRef | 性能优化 |
| 多层对象(需深度响应) | ref | 完整响应式支持 |
| 多层对象(性能敏感) | shallowRef | 减少响应式开销 |
| 第三方对象/DOM | shallowRef | 避免不必要的代理 |
最佳实践建议
- 有序列表默认使用
ref,除非有明确的性能需求 - 对于频繁更新的大数据或深层嵌套对象,考虑
shallowRef - 使用
shallowRef时,配合triggerRef()手动控制更新时机 - 监控性能时,如果发现响应式开销过大,考虑将部分数据改为
shallowRef
// 混合使用示例
const state = {
// 深度响应式数据
user: ref({ profile: { /* ... */ } }),
// 浅响应式大数据
largeList: shallowRef([/* ... */]),
// 基础数据
count: ref(0)
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
记住:过早优化是万恶之源。先使用ref实现功能,遇到性能瓶颈时再考虑shallowRef优化。