- Published on
理解 Vue3 的各类 ref: toRef, toRefs, isRef, unref等
- Authors
- Name
- Deng Hua
在Vue3中,有很多与响应性相关的函数,例如toRef、toRefs、isRef、unref等,合理使用这些函数可以在实际开发中大大提高效率。
目录
ref
核心API之一,接受一个值并返回一个响应对象。我们可以通过 .value
访问或修改这个值。在模板中,我们可以省略 .value
,例如下面的代码中,当点击按钮时,页面上的计数会响应变化。
<template>
<div>
{{ count }}
<button @click="addCount">+1</button>
</div>
</template>
<script lang='ts' setup>
import { ref } from "vue"
const count = ref(1)
const addCount = () => {
count.value++
}
</script>
toRef
toRef
可以根据响应式对象中的属性来创建响应式引用。同时,这个 ref
与原始对象中的属性同步。
如果原始对象属性的值发生变化,ref
也会相应变化,反之亦然。它接受两个参数,一个是对应的响应式对象,另一个是属性值,如下面的代码。
<template>
<div>
{{ count.a }}
{{ a }}
<button @click="addCount">+1</button>
</div>
</template>
<script lang='ts' setup>
import { ref, toRef } from "vue";
const count = ref({
a: 1,
b: 2
})
const a = toRef(count.value, 'a')
const addCount = () => {
a.value++
}
</script>
当点击按钮修改a
的值时,count
中的a
也会改变。当然,这里的count
也可以使用reactive
代替。
toRefs
toRefs
可以将响应式对象转换为普通对象,而这个普通对象的每个属性都是一个响应式 ref
。
<template>
<div>
{{ count.a }}
{{ countAsRefs.a }}
<button @click="addCount">+1</button>
</div>
</template>
<script lang='ts' setup>
import { reactive, toRefs } from "vue"
const count = reactive({
a: 1,
b: 2
})
const countAsRefs = toRefs(count)
const addCount = () => {
countAsRefs.a.value++
}
</script>
此时代码中 countAsRefs
的类型为
{
a: Ref<number>,
b: Ref<number>
}
它的属性 a
和 b
是响应式ref对象。同样,它们也与原始count
对象的属性同步。
根据它的特点,我们通常用它来解构一个响应式对象,同时不会失去其响应性。
import { reactive, toRefs } from "vue";
const count = reactive({
a: 1,
b: 2,
});
const { a, b } = toRefs(count);
此时, a
和 b
都是响应式 ref 对象,并且与原始对象 count
的 a
和 b
属性同步。
isRef
顾名思义, isRef
用于确定值是否为 ref 对象。
注意:它无法确定该值是否是响应性的(isReactive
可用于此目的)。
import { reactive, isRef, ref } from "vue";
const count = ref(1);
const testObj = reactive({
a: 1,
});
console.log(isRef(count)); //true
console.log(isRef(testObj)); //false
isRef
返回值也是一个类型判定 (type predicate),这意味着 isRef
可以被用作类型守卫:
let foo: unknown
if (isRef(foo)) {
// foo 的类型被收窄为了 Ref<unknown>
foo.value
}
unref()
事实上,它是一种语法糖。
val = isRef(val) ? val.value : val;
如果它是 ref
,它将返回其内部值,否则,它将返回自身。
这种语法糖表明它可以删除响应性对象的响应性应用。例如,如果我们只想获得一个响应式值但不希望它是响应性的,我们可以使用它来取消引用。
例如:
<template>
<div>
{{ unRefAsCount }}
{{ count }}
<button @click="addCount">+1</button>
</div>
</template>
<script lang='ts' setup>
import { unref, ref } from "vue"
const count = ref(1)
let unRefAsCount = unref(count)
const addCount = () => {
count.value++
}
</script>
unRefAsCount
现在没有响应性。
shallowRef
从字面意思来看,我们可以看出它是一个ref()
的浅引用。
什么是浅引用?与 ref()
的区别在于,只有 .value
是响应式的,而更深层次的属性不是响应性的。
<template>
<div>
{{ shallowObj.a }}
<button @click="addCount"> +1</button>
</div>
</template>
<script lang='ts' setup>
import { shallowRef } from "vue"
const shallowObj = shallowRef({
a: 1
})
const addCount = () => {
// 不会触发页面更新
shallowObj.value.a++
}
</script>
但是,如果我们更改 addCount
来修改整个 .value
,它将触发响应性更新。
const addCount = () => {
let temp = shallowObj.value.a;
temp++;
shallowObj.value = {
a: temp,
};
};
triggerRef
它允许 shallowRef
在其深层属性发生变化时强制触发更改。例如,在上面的代码示例中添加 triggerRef
就无法触发反应性。
<template>
<div>
{{ shallowObj.a }}
<button @click="addCount"> +1</button>
</div>
</template>
<script lang='ts' setup>
import { shallowRef, triggerRef } from "vue"
const shallowObj = shallowRef({
a: 1
})
const addCount = () => {
shallowObj.value.a++
// 添加“triggerRef”以强制更改。
triggerRef(shallowObj)
}
</script>
customRef
顾名思义,它是一个自定义的ref。我们可以通过 customRef
显式跟踪某个值的反应变化。
它接受一个函数,该函数以 track
和 trigger
作为参数,并返回一个具有 get
和 set
方法的对象。
例如,封装一个自定义响应性对象myRef
并对其进行控制,使其仅在小于 4
时触发响应性。
<template>
<div>
{{ count }}
<button @click="addCount"> +1</button>
</div>
</template>
<script lang='ts' setup>
import { customRef } from "vue"
const myRef = (value: number) => {
const customValue = customRef((track, trigger) => {
return {
get() {
track() // 告诉Vue需要跟踪后续内容的变化
return value;
},
set(newValue) {
console.log(newValue); // myRef.value = xxx
// 添加“trigger”会触发响应式更新,这里仅在值小于4时触发。
if (value < 4) trigger()
value = newValue
}
}
})
return customValue
}
const count = myRef(0)
const addCount = () => {
count.value++
}
</script>
当 count
大于4时,它失去响应性。
End.