使用響應式數據的主要目的是讓數據發生改變的時候實時地改變模板中的顯示效果,隨之帶來的是可以使用計算屬性、監聽器等便利的輔助功能。
reactive
是一個函數,它接收一個普通對象然后返回該普通對象的響應式代理。
const obj = reactive({ count: 0 })
let todos = reactive([
{id: 1, title: '任務一', completed: false},
{id: 2, title: '任務二', completed: true},
{id: 3, title: '任務三', completed: false}
]);
返回的對象是傳入對象的映射,返回的代理對象不等于原始對象,但響應式轉換是“深層的”:會響應對象內部所有嵌套的屬性,不會因為嵌套過多而失去響應。所以僅使用代理對象而避免依賴原始對象即可。
ref
是一個函數,它返回一個響應式且可改變的 ref 對象,可選接收的對象參數作為默認值。ref 對象擁有一個指向內部值的單一屬性 .value
:
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
如果默認值是普通類型則直接響應,如果是對象類型則調用 reactive 方法進行深層響應轉換。
在模板中訪問
當 ref 作為渲染上下文的屬性返回(即在setup() 返回的對象中)并在模板中使用時,它會自動解套,無需在模板內額外書寫 .value:
<template>
<div>{{ count }}</div>
</template>
<script>
export default {
setup() {
return {
count: ref(0),
}
},
}
</script>
作為響應式對象的屬性訪問
當 ref 作為 reactive 對象的 property 被訪問或修改時,也將自動解套 value 值,其行為類似普通屬性:
const count = ref(0)
const state = reactive({
count,
})
console.log(state.count) // 0
state.count = 1
console.log(count.value) // 1
注意如果將一個新的 ref 分配給現有的 ref, 將替換舊的 ref:
const otherCount = ref(2)
state.count = otherCount
console.log(state.count) // 2
console.log(count.value) // 1
注意當嵌套在 reactive Object 中時,ref 才會解套。從 Array 或者 Map 等原生集合類中訪問 ref 時,不會自動解套:
const arr = reactive([ref(0)])
// 這里需要 .value
console.log(arr[0].value)
const map = reactive(new Map([['foo', ref(0)]]))
// 這里需要 .value
console.log(map.get('foo').value)
computed
Vue3的計算屬性,是個函數,用法有兩種。
- 傳入一個 getter 函數,返回一個默認不可手動修改的 ref 對象。
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // 錯誤!
- 傳入一個擁有 get 和 set 函數的對象,創建一個可手動修改的計算狀態。
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: (val) => {
count.value = val - 1
},
})
plusOne.value = 1
console.log(count.value) // 0
effect
也就是監聽器——當某響應式數據發生改變的時候,執行相應的副作用。實際使用時有兩種函數watchEffect和watch。
watchEffect
立即執行傳入的一個函數,并響應式追蹤其依賴,并在其依賴變更時重新運行該函數。
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> 打印出 0
setTimeout(() => {
count.value++
// -> 打印出 1
}, 100)
watchEffect
在追蹤依賴方面類似于computed
,從傳入的函數體內部追蹤依賴的響應式數據。相較于computed
,watchEffect
不需要返回值。
watch
watch
API 完全等效于 2.x this.$watch
(以及 watch 中相應的選項)。與watchEffect
比較,特點有:
- 懶執行副作用,定義完成后不會立即執行;
- 偵聽特定的數據源,更明確哪些狀態的改變會觸發偵聽器重新運行副作用;
- 訪問偵聽狀態變化前后的值。
數據源可以監聽多個或單個:
- 偵聽單個數據源
偵聽器的數據源可以是一個擁有返回值的 getter 函數,也可以是 ref:
// 偵聽一個 getter
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
// 直接偵聽一個 ref
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
- 偵聽多個數據源
watcher 也可以使用數組來同時偵聽多個源:
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
兩種effect共有的行為
#停止偵聽
const stop = watchEffect(() => {
/* ... */
})
// 之后
stop()
#清除副作用
watchEffect((onInvalidate) => {
const token = performAsyncOperation(id.value)
onInvalidate(() => {
// id 改變時 或 停止偵聽時
// 取消之前的異步操作
token.cancel()
})
})