【shallowRef 与 shallowReactive 】
shallowRef
- 作用:创建一个响应式数据,但只对顶层属性进行响应式处理。
- 用法:
let myVar = shallowRef(initialValue);
- 特点:只跟踪引用值的变化,不关心值内部的属性变化。
shallowReactive
- 作用:创建一个浅层响应式对象,只会使对象的最顶层属性变成响应式的,对象内部的嵌套属性则不会变成响应式的
- 用法:
const myObj = shallowReactive({ ... });
- 特点:对象的顶层属性是响应式的,但嵌套对象的属性不是。
总结
通过使用 shallowRef()
和 shallowReactive()
来绕开深度响应。浅层式 API
创建的状态只在其顶层是响应式的,对所有深层的对象不会做任何处理,避免了对每一个内部属性做响应式所带来的性能成本,这使得属性的访问变得更快,可提升性能。
【readonly 与 shallowReadonly】
readonly
- 作用:用于创建一个对象的深只读副本。
- 用法:
const original = reactive({ ... });
const readOnlyCopy = readonly(original);
-
特点:
- 对象的所有嵌套属性都将变为只读。
- 任何尝试修改这个对象的操作都会被阻止(在开发模式下,还会在控制台中发出警告)。
-
应用场景:
- 创建不可变的状态快照。
- 保护全局状态或配置不被修改。
shallowReadonly
- 作用:与
readonly
类似,但只作用于对象的顶层属性。 - 用法:
const original = reactive({ ... });
const shallowReadOnlyCopy = shallowReadonly(original);
-
特点:
- 只将对象的顶层属性设置为只读,对象内部的嵌套属性仍然是可变的。
- 适用于只需保护对象顶层属性的场景。
【toRaw 与 markRaw】
toRaw
-
作用:用于获取一个响应式对象的原始对象,
toRaw
返回的对象不再是响应式的,不会触发视图更新。- 官网描述:这是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法。不建议保存对原始对象的持久引用,请谨慎使用。
- 何时使用? —— 在需要将响应式对象传递给非
Vue
的库或外部系统时,使用toRaw
可以确保它们收到的是普通对象
- 具体编码:
import { reactive,toRaw,markRaw,isReactive } from "vue";
/* toRaw */
// 响应式对象
let person = reactive({name:'tony',age:18})
// 原始对象
let rawPerson = toRaw(person)
/* markRaw */
let citysd = markRaw([
{id:'asdda01',name:'北京'},
{id:'asdda02',name:'上海'},
{id:'asdda03',name:'天津'},
{id:'asdda04',name:'重庆'}
])
// 根据原始对象citys去创建响应式对象citys2 —— 创建失败,因为citys被markRaw标记了
let citys2 = reactive(citys)
console.log(isReactive(person))
console.log(isReactive(rawPerson))
console.log(isReactive(citys))
console.log(isReactive(citys2))
markRaw
-
作用:标记一个对象,使其永远不会变成响应式的。
- 例如使用
mockjs
时,为了防止误把mockjs
变为响应式对象,可以使用markRaw
去标记mockjs
- 例如使用
- 编码:
/* markRaw */
let citys = markRaw([
{id:'asdda01',name:'北京'},
{id:'asdda02',name:'上海'},
{id:'asdda03',name:'天津'},
{id:'asdda04',name:'重庆'}
])
// 根据原始对象citys去创建响应式对象citys2 —— 创建失败,因为citys被markRaw标记了
let citys2 = reactive(citys)
【customRef】
作用:创建一个自定义的 ref
,并对其依赖项跟踪和更新触发进行逻辑控制。
实现防抖效果(useSumRef.ts
):
<script lang="ts" setup>
import { customRef } from 'vue';
function debouncedRef(initialValue, delay) {
let timeoutId;
return customRef((track, trigger) => ({
get() {
// 使用 track 函数标记依赖
track();
return initialValue;
},
set(newValue) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
initialValue = newValue;
// 使用 trigger 函数触发依赖更新
trigger();
}, delay);
}
}));
}
// 使用自定义的ref
const myDebouncedRef = debouncedRef('Hello World', 500);
</script>
debouncedRef 是一个自定义的 ref 工厂函数,它接收两个参数:初始值和延迟时间。当 set 方法被调用时,会清除之前的计时器并设置一个新的计时器,在延迟时间结束后更新值并触发依赖更新。
功能详解
防抖(Debounce)是一种在指定时间内忽略多次触发,只在最后一次触发后的一段时间内执行的技术。上面的 debouncedRef 实现了这一功能,当我们频繁更新 ref 的值时,只有在最后一次更新后的延迟时间结束后,值才会被真正更新。
关键函数 track 和 trigger
- track:用于标记依赖,告诉 Vue 这个 ref 的值被读取了。
- trigger:用于触发依赖更新,告诉 Vue 这个 ref 的值被修改了。
异步更新
<script lang="ts" setup>
import { customRef } from 'vue';
function asyncRef(initialValue, asyncFunction) {
return customRef((track, trigger) => ({
get() {
track();
return initialValue;
},
async set(newValue) {
initialValue = await asyncFunction(newValue);
trigger();
}
}));
}
// 使用自定义的ref
const myAsyncRef = asyncRef('Initial Value', async (value) => {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 1000));
return value.toUpperCase();
});
</script>
条件性更新
<script lang="ts" setup>
import { customRef } from 'vue';
function conditionalRef(initialValue, conditionFunction) {
return customRef((track, trigger) => ({
get() {
track();
return initialValue;
},
set(newValue) {
if (conditionFunction(newValue)) {
initialValue = newValue;
trigger();
}
}
}));
}
// 使用自定义的ref
const myConditionalRef = conditionalRef(0, value => value >= 0);
</script>