ref
接受一個內部值並返回一個響應式且可變的 ref 對象。ref 對象僅有一個 .value
property,指向該內部值。
<script setup>
import { ref } from "vue";
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
</script>
如果將對象分配為 ref 值,則它將被 reactive 函數處理為深層的響應式對象。
類型聲明:
interface Ref<T> {
value: T
}
function ref<T>(value: T): Ref<T>
有時我們可能需要為 ref 的內部值指定復雜類型。可以在調用 ref
時傳遞一個泛型參數以覆蓋默認推斷,從而簡潔地做到這一點:
const foo = ref<string | number>('foo') // foo 的類型:Ref<string | number>
foo.value = 123 // ok!
如果泛型的類型未知,則建議將 ref
轉換為 Ref<T>
:
function useState<State extends string>(initial: State) {
const state = ref(initial) as Ref<State> // state.value -> State extends string
return state
}
unref
如果參數是一個 ref
,則返回內部值,否則返回參數本身。這是 val = isRef(val) ? val.value : val
的語法糖函數。
function useFoo(x: number | Ref<number>) {
const unwrapped = unref(x) // unwrapped 現在一定是數字類型
}
toRef
<script setup>
import { reactive, toRef } from "vue";
const state = reactive({
foo: 1,
bar: 2
})
const fooRef = toRef(state, 'foo')
fooRef.value++
console.log(state.foo) // 2
state.foo++
state.bar--
console.log(fooRef.value) // 3
</script>
當你要將 prop 的 ref 傳遞給復合函數時,toRef
很有用:
export default {
setup(props) {
useSomeFeature(toRef(props, 'foo'))
}
}
即使源 property 不存在,toRef
也會返回一個可用的 ref。這使得它在使用可選 prop 時特別有用,可選 prop 並不會被 toRefs
處理。
toRefs
將響應式對象轉換為普通對象,其中結果對象的每個 property 都是指向原始對象相應 property 的 ref
。
<script setup>
import { reactive, toRefs } from "vue";
const state = reactive({
foo: 1,
bar: 2
})
const stateAsRefs = toRefs(state)
/*
stateAsRefs 的類型:
{
foo: Ref<number>,
bar: Ref<number>
}
*/
// ref 和原始 property 已經“鏈接”起來了
state.foo++
console.log(stateAsRefs.foo.value) // 2
stateAsRefs.foo.value++
console.log(state.foo) // 3
</script>
當從組合式函數返回響應式對象時,toRefs
非常有用,這樣消費組件就可以在不丟失響應性的情況下對返回的對象進行解構/展開:
<script >
import { reactive, toRefs } from "vue";
function useFeatureX() {
const state = reactive({
foo: 1,
bar: 2,
});
// 操作 state 的邏輯
// 返回時轉換為ref
return toRefs(state);
}
export default {
setup() {
// 可以在不失去響應性的情況下解構
const { foo, bar } = useFeatureX();
return {
foo,
bar,
};
},
};
</script>
toRefs
只會為源對象中包含的 property 生成 ref。如果要為特定的 property 創建 ref,則應當使用 toRef
isRef
檢查值是否為一個 ref 對象。
customRef
創建一個自定義的 ref,並對其依賴項跟蹤和更新觸發進行顯式控制。它需要一個工廠函數,該函數接收 track
和 trigger
函數作為參數,並且應該返回一個帶有 get
和 set
的對象。
使用自定義 ref 通過 v-model
實現 debounce 的示例:
<input v-model="text" />
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}
export default {
setup() {
return {
text: useDebouncedRef('hello')
}
}
}
類型聲明:
function customRef<T>(factory: CustomRefFactory<T>): Ref<T>
type CustomRefFactory<T> = (
track: () => void,
trigger: () => void
) => {
get: () => T
set: (value: T) => void
}
shallowRef
創建一個跟蹤自身 .value
變化的 ref,但不會使其值也變成響應式的。
const foo = shallowRef({})
// 改變 ref 的值是響應式的
foo.value = {}
// 但是這個值不會被轉換。
isReactive(foo.value) // false
triggerRef
手動執行與 shallowRef
關聯的任何作用 (effect)。
const shallow = shallowRef({
greet: 'Hello, world'
})
// 第一次運行時記錄一次 "Hello, world"
watchEffect(() => {
console.log(shallow.value.greet)
})
// 這不會觸發作用 (effect),因為 ref 是淺層的
shallow.value.greet = 'Hello, universe'
// 記錄 "Hello, universe"
triggerRef(shallow)
免責聲明
本文只是在學習Vue 響應式API的一些筆記,文中的資料也會涉及到引用,具體出處不詳,商業用途請謹慎轉載。