Vue3 浅层响应式 API
1. ref vs shallowRef
1.1 基本概念
- ref: 深层响应式,会递归地将对象的所有属性转换为响应式
- shallowRef: 浅层响应式,只有 .value 的改变会触发更新,不会递归转换对象的属性
1.2 使用对比
js
复制代码
// ref 示例
const deepRef = ref({
count: 0,
nested: {
value: 'hello'
}
})
// 这些修改都会触发更新
deepRef.value.count++ // 触发更新
deepRef.value.nested.value = 'world' // 触发更新
// shallowRef 示例
const shallowValue = shallowRef({
count: 0,
nested: {
value: 'hello'
}
})
// 只有直接替换 .value 才会触发更新
shallowValue.value.count++ // 不会触发更新
shallowValue.value.nested.value = 'world' // 不会触发更新
shallowValue.value = { count: 1 } // 触发更新
1.3 性能优化示例
vue
复制代码
<template>
<div>
<video ref="videoRef" :src="videoUrl"></video>
<button @click="updateVideoUrl">更新视频</button>
</div>
</template>
<script setup>
import { shallowRef } from 'vue'
// 使用 shallowRef 优化大型数据或 DOM 引用
const videoRef = shallowRef(null)
const videoUrl = shallowRef('https://example.com/video.mp4')
const updateVideoUrl = () => {
// 直接更新 .value 触发更新
videoUrl.value = 'https://example.com/new-video.mp4'
}
</script>
2. reactive vs shallowReactive
2.1 基本概念
- reactive: 深层响应式,递归地将所有嵌套对象转换为响应式
- shallowReactive: 浅层响应式,只将对象的第一层属性转换为响应式
2.2 使用对比
js
复制代码
// reactive 示例
const deepState = reactive({
count: 0,
nested: {
value: 'hello'
}
})
// 这些修改都会触发更新
deepState.count++ // 触发更新
deepState.nested.value = 'world' // 触发更新
// shallowReactive 示例
const shallowState = shallowReactive({
count: 0,
nested: {
value: 'hello'
}
})
// 只有第一层属性的改变会触发更新
shallowState.count++ // 触发更新
shallowState.nested.value = 'world' // 不会触发更新
shallowState.nested = { value: 'world' } // 触发更新
2.3 实际应用示例
vue
复制代码
<template>
<div>
<h2>用户信息</h2>
<div>姓名: {{ userInfo.name }}</div>
<div>年龄: {{ userInfo.age }}</div>
<!-- 不需要追踪 metadata 的变化 -->
<div>元数据: {{ userInfo.metadata.lastUpdated }}</div>
</div>
</template>
<script setup>
import { shallowReactive } from 'vue'
// 使用 shallowReactive 优化性能,metadata 的变化不需要触发更新
const userInfo = shallowReactive({
name: 'John',
age: 30,
metadata: {
lastUpdated: new Date(),
visits: 0
}
})
// 只有顶层属性的变化会触发更新
const updateUser = () => {
userInfo.name = 'Jane' // 触发更新
userInfo.metadata.visits++ // 不会触发更新
}
</script>
3. 使用场景对比
3.1 适合使用深层响应式(ref/reactive)的场景
- 表单数据
js
复制代码
const formData = reactive({
user: {
name: '',
email: '',
preferences: {
newsletter: true,
notifications: {
email: true,
sms: false
}
}
}
})
- 需要监听所有层级变化的数据
js
复制代码
const settings = ref({
theme: {
dark: false,
colors: {
primary: '#000',
secondary: '#fff'
}
}
})
3.2 适合使用浅层响应式(shallowRef/shallowReactive)的场景
- 大型数据结构且只需要监听顶层变化
js
复制代码
const bigData = shallowRef({
items: new Array(10000).fill(0).map((_, i) => ({
id: i,
data: { /* 大量数据 */ }
}))
})
// 整体替换数据时才触发更新
const updateData = () => {
bigData.value = newBigData
}
- 外部库或 DOM 引用
js
复制代码
const chartInstance = shallowRef(null)
const mapInstance = shallowRef(null)
onMounted(() => {
chartInstance.value = new ThirdPartyChart()
mapInstance.value = new ThirdPartyMap()
})
- 不需要深层响应式的状态管理
js
复制代码
const state = shallowReactive({
ui: {
loading: false,
error: null
},
cache: new Map(), // 不需要响应式的缓存数据
helpers: {
formatter: () => {}, // 工具函数不需要响应式
}
})
4. 性能优化建议
- 选择合适的响应式 API
js
复制代码
// ✅ 大型数据使用浅层响应式
const bigData = shallowRef(largeDataSet)
// ❌ 不必要的深层响应式
const bigData = ref(largeDataSet)
- 避免不必要的响应式转换
js
复制代码
// ✅ 静态数据使用浅层响应式
const config = shallowReactive({
constants: { /* 大量静态配置 */ },
settings: { /* 需要响应式的设置 */ }
})
// ❌ 对静态数据使用深层响应式
const config = reactive({
constants: { /* 大量静态配置 */ },
settings: { /* 需要响应式的设置 */ }
})
- 合理组合使用
js
复制代码
// 混合使用深层和浅层响应式
const state = reactive({
// 需要深层响应式的数据
userSettings: {
theme: 'dark',
notifications: { /* ... */ }
},
// 使用 shallowRef 包装大型数据
bigData: shallowRef(largeDataSet)
})
5. 注意事项
- 响应式丢失问题
js
复制代码
const shallow = shallowReactive({
nested: {
count: 0
}
})
// 解构会失去响应性
const { nested } = shallow
nested.count++ // 不会触发更新
- 替换整个对象
js
复制代码
const data = shallowRef({
nested: {
value: 0
}
})
// 需要替换整个对象才能触发更新
data.value = {
nested: {
value: 1
}
}
- 与计算属性配合
js
复制代码
// 当只需要监听部分数据变化时,使用计算属性
const data = shallowReactive({
items: [],
metadata: { /* ... */ }
})
// 只监听 items 的变化
const computedValue = computed(() => {
return processItems(data.items)
})