Vue3中shallwRef和shallshallReactive使用
在 Vue 3 中,
shallowRef和shallowReactive是用于优化性能的响应式 API,它们与ref和 reactive 的主要区别在于它们对嵌套数据的处理方式。
shallowRef
通俗理解
shallowRef是一种特殊的ref,它只会在引用(内存地址)发生变化时触发响应式更新,而不会追踪对象内部属性的变化。
类比
想象你有一个文件夹,文件夹里有很多文件。shallowRef只会关注你是否更换了整个文件夹,而不会关心文件夹内部的文件是否被修改。
特点
- 只追踪引用变化:只有当你完全替换掉
shallowRef的值时,才会触发响应式更新。 - 不追踪嵌套属性:如果你修改了
shallowRef的某个嵌套属性,不会触发响应式更新。
使用场景
- 性能优化:当你有一个很大的对象,但只需要在完全替换对象时触发更新,可以使用
shallowRef。 - 动态组件:例如在切换组件时,你可以用 shallowRef 来存储当前组件的引用。
示例
import { shallowRef } from 'vue';
const state = shallowRef({ count: 1 });
// 不会触发更新
state.value.count = 2;
// 会触发更新
state.value = { count: 2 };
shallowReactive
通俗理解
shallowReactive是一种特殊的reactive,它只会让对象的第一层属性响应式,而不会递归地让嵌套对象或数组响应式。
类比
想象你有一个文件夹,文件夹里有很多文件和子文件夹。shallowReactive只会关注文件夹里的文件,但不会关注子文件夹里的内容。
特点
- 只追踪第一层属性:只有当你修改对象的第一层属性时,才会触发响应式更新。
- 嵌套对象不响应式:如果你修改了嵌套对象的属性,不会触发响应式更新。
使用场景
- 性能优化:当你有一个很大的对象,但只有第一层属性会频繁变化时,可以使用
shallowReactive。 - 避免深层嵌套的响应式:如果你不希望嵌套对象也变成响应式,可以使用
shallowReactive。
示例
import { shallowReactive } from 'vue';
const state = shallowReactive({
count: 1,
nested: { value: 10 }
});
// 会触发更新
state.count = 2;
// 不会触发更新
state.nested.value = 20;
shallowRef和shallowReactive的区别
shallowRef
- 适用对象:单个值(可以是对象、数组或基本类型)。
- 触发更新:只有在完全替换
value时才会触发更新。 - 嵌套属性:不会追踪嵌套属性的变化。
shallowReactive
- 适用对象:对象或数组。
- 触发更新:只有在修改第一层属性时才会触发更新。
- 嵌套属性:嵌套对象不会变成响应式。
示例对比
const myRef = shallowRef({ name: 'Paul', more: {} });
const myReactive = shallowReactive({ name: 'Paul', more: {} });
// myRef 不会解包 ref
myRef.value.name = 'Jack'; // 不触发更新
myRef.value = { name: 'Jack' }; // 触发更新
// myReactive 会解包第一层
myReactive.name = 'Jack'; // 触发更新
myReactive.more.name = 'Jack'; // 不触发更新
总结
shallowRef:只在引用变化时触发更新,不追踪嵌套属性的变化。shallowReactive:只在第一层属性变化时触发更新,嵌套对象不会变成响应式。它们主要用于性能优化,当你不需要让整个对象或嵌套对象都变成响应式时,可以使用它们。
readonly和shallowReadonly
在 Vue 3 中,
readonly和shallowReadonly是用于创建只读数据的 API,它们可以帮助开发者保护数据不被意外修改。
readonly(深度只读)
通俗理解
readonly会将一个对象的所有属性(包括嵌套属性)都变成只读的。这意味着你不能修改任何一层的属性值,否则会抛出错误。
类比
想象你有一个多层的保险箱,每一层都有锁。readonly就是把每一层的锁都锁上,确保里面的任何东西都不能被修改。
特点
- 深度只读:递归地将对象的所有嵌套属性都变成只读的。
- 不可修改:尝试修改任何属性都会被阻止,并在开发模式下抛出警告。
- 适用于保护整个数据结构:当你希望保护整个对象不被修改时,使用
readonly。
示例
import { reactive, readonly } from 'vue';
const originalData = reactive({
name: 'Alice',
details: {
age: 25,
address: '123 Main St'
}
});
const readOnlyData = readonly(originalData);
// 尝试修改第一层属性
readOnlyData.name = 'Bob'; // 报错:Cannot assign to 'name' because it is a read-only property.
// 尝试修改嵌套属性
readOnlyData.details.age = 30; // 报错:Cannot assign to 'age' because it is a read-only property.
shallowReadonly(浅层只读)
通俗理解
shallowReadonly只会将对象的第一层属性变成只读的,而不会影响嵌套对象的内部属性。这意味着你可以修改嵌套对象的属性,但不能修改第一层的属性。
类比
想象你有一个多层的保险箱,只有最外层的锁被锁上了,而里面的层没有锁。你可以修改里面的任何东西,但不能直接替换整个外层保险箱。
特点
- 浅层只读:只将对象的第一层属性变成只读的,嵌套对象的属性可以被修改。
- 适用于保护顶层结构:当你希望保护对象的顶层结构不被替换,但允许修改嵌套数据时,使用
shallowReadonly。
示例
import { reactive, shallowReadonly } from 'vue';
const originalData = reactive({
name: 'Alice',
details: {
age: 25,
address: '123 Main St'
}
});
const shallowReadOnlyData = shallowReadonly(originalData);
// 尝试修改第一层属性
shallowReadOnlyData.name = 'Bob'; // 报错:Cannot assign to 'name' because it is a read-only property.
// 修改嵌套属性(不会报错)
shallowReadOnlyData.details.age = 30; // 可以正常修改
shallowReadOnlyData.details.address = '456 Elm St'; // 可以正常修改
readonly和shallowReadonly的区别
| 特性 | readonly | shallowReadonly |
|---|---|---|
| 作用范围 | 深度只读(递归) | 浅层只读(只限制第一层) |
| 是否影响嵌套属性 | 嵌套属性也变成只读 | 嵌套属性可以被修改 |
| 适用场景 | 需要保护整个数据结构不被修改 | 需要保护顶层结构,但允许修改嵌套数据 |
| 性能 | 更重(递归处理) | 更轻(只处理第一层) |
使用场景
readonly的使用场景
- 当你希望保护整个数据结构不被修改时,例如从外部获取的数据需要保持不变。
- 当你需要确保数据的不可变性时,例如在某些状态管理场景中。
shallowReadonly的使用场景 - 当你只需要保护对象的顶层结构,但允许修改嵌套数据时。
- 当你需要优化性能,避免递归处理整个对象时。
总结
readonly:深度只读,递归地将所有属性变成只读,适用于保护整个数据结构。shallowReadonly:浅层只读,只将第一层属性变成只读,适用于保护顶层结构但允许修改嵌套数据。
选择哪一个取决于你的具体需求:如果你需要保护整个数据结构,就用readonly;如果你只需要保护顶层结构,就用shallowReadonly。
toRaw和markRaw使用
在 Vue 3 中,
toRaw和markRaw是两个用于处理响应式数据的工具函数,它们可以帮助开发者更精细地控制数据的响应式行为。
toRaw
通俗理解
toRaw是一个函数,用于获取一个响应式对象的原始(非响应式)对象。它可以帮助你绕过 Vue 的响应式系统,直接操作原始数据。
类比
想象你有一个带魔法的盒子(响应式对象),toRaw就是把魔法去掉,让你看到盒子里的真实东西(原始对象)。
特点
- 返回原始对象:
toRaw返回的对象是非响应式的,修改它不会触发 Vue 的响应式更新。 - 临时操作:通常用于临时获取原始数据,而不影响响应式系统。
使用场景
直接操作原始数据: 当你需要直接修改数据而不触发响应式更新时,例如在性能敏感的代码中。
import { reactive, toRaw } from 'vue';
const state = reactive({ count: 0 });
const rawState = toRaw(state);
rawState.count = 10; // 修改原始对象,不会触发响应式更新
console.log(state.count); // 输出仍然是 0
与第三方库交互: 当你需要将数据传递给不支持 Vue 响应式系统的第三方库时。
const state = reactive({ data: [1, 2, 3] });
const rawState = toRaw(state);
thirdPartyLibrary(rawState.data); // 传递原始数据
markRaw
通俗理解
markRaw是一个函数,用于标记一个对象,使其永远不会被转换为响应式对象。即使将这个对象传递给reactive或ref,它也不会变成响应式的。
类比
想象你有一个普通盒子(非响应式对象),markRaw就是给盒子贴上一个标签,告诉 Vue:“这个盒子不需要魔法!”即使 Vue 想把它变成魔法盒子,也不会成功。
特点
- 保持非响应式:标记后的对象永远不会变成响应式对象。
- 不可逆:一旦标记为
markRaw,对象将永远保持非响应式。
使用场景
不需要响应式的数据结构: 当你明确不希望某个对象被转换为响应式对象时,例如工具类或静态配置。
import { reactive, markRaw } from 'vue';
class Helper {
format(value) {
return `Formatted: ${value}`;
}
}
const helper = markRaw(new Helper());
const state = reactive({
helper,
data: 'test'
});
console.log(state.helper.format(state.data)); // 不会触发响应式更新
避免不必要的响应式转换: 当你需要将一个对象传递给 Vue 的响应式系统,但希望保持它的非响应式状态时。
const constants = markRaw({
API_ENDPOINT: '/api/data',
APP_NAME: 'MyApp'
});
const state = reactive({
config: constants
});
console.log(state.config.API_ENDPOINT); // 不会变成响应式
toRaw和markRaw的区别
| 特性 | toRaw | markRaw |
|---|---|---|
| 作用 | 获取响应式对象的原始对象 | 标记一个对象,使其永远不会变成响应式 |
| 返回值 | 原始对象 | 被标记的对象 |
| 响应式行为 | 返回的对象是非响应式的 | 被标记的对象不会变成响应式 |
| 使用场景 | 需要直接操作原始数据 | 需要保持对象的非响应式状态 |
总结
toRaw
- 用于获取响应式对象的原始对象。
- 适用于需要直接操作原始数据或与第三方库交互的场景。
markRaw
- 用于标记一个对象,使其永远不会变成响应式对象。
- 适用于需要保持对象非响应式状态的场景,例如工具类、静态配置或大型不可变对象。
通过合理使用toRaw和markRaw,可以更灵活地控制 Vue 的响应式系统,优化性能并避免不必要的响应式转换。