Computed和Watch的區別
時間:2023-09-11 來源:華清遠見
一、概念
1.computed
Vue中用計算屬性來描述依賴響應式狀態的復雜邏輯.模板中的表達式雖然方便,但也只能用來做簡單的操作。如果在模板中寫太多邏輯,會讓模板變得臃腫,難以維護。比如說,我們有這樣一個包含嵌套數組的對象:
constauthor=reactive({
name:'JohnDoe',
books:[
'Vue2-AdvancedGuide',
'Vue3-BasicGuide',
'Vue4-TheMystery'
]
})
我們想根據author是否已有一些書籍來展示不同的信息:
<p>Haspublishedbooks:</p>
<span>{{author.books.length>0?'Yes':'No'}}</span>
這里的模板看起來有些復雜。我們必須認真看好一會兒才能明白它的計算依賴于author.books。更重要的是,如果在模板中需要不止一次這樣的計算,我們
可不想將這樣的代碼在模板里重復好多遍。因此我們推薦使用計算屬性來描述依賴響應式狀態的復雜邏輯。這是重構后的示例:
<scriptsetup>
import{reactive,computed}from'vue'
constauthor=reactive({
name:'JohnDoe',
books:[
'Vue2-AdvancedGuide',
'Vue3-BasicGuide',
'Vue4-TheMystery'
]
})
//一個計算屬性ref
constpublishedBooksMessage=computed(()=>{
returnauthor.books.length>0?'Yes':'No'
})
</script>
<template>
<p>Haspublishedbooks:</p>
<span>{{publishedBooksMessage}}</span>
</template>
我們在這里定義了一個計算屬性publishedBooksMessage。computed()方法期望接收一個getter函數,返回值為一個計算屬性ref。和其他一般的ref類似,你可以通過publishedBooksMessage.value訪問計算結果。計算屬性ref也會在模板中自動解包,因此在模板表達式中引用時無需添加.value。
Vue的計算屬性會自動追蹤響應式依賴。它會檢測到publishedBooksMessage依賴于author.books,所以當author.books改變時,任何依賴于publishedBooksMessage的綁定都會同時更新。
2.Watch
在Vue中watch代表偵聽器,我們可以使用watch函數在每次響應式狀態發生變化時觸發回調函數計算屬性允許我們聲明性地計算衍生值。然而在有些情況下,我們需要在狀態變化時執行一些“副作用”:例如更改DOM,或是根據異步操作的結果去修改另一處的狀態。
<scriptsetup>
import{ref,watch}from'vue'
constquestion=ref('')
constanswer=ref('Questionsusuallycontainaquestionmark.;-)')
//可以直接偵聽一個ref
watch(question,async(newQuestion,oldQuestion)=>{
if(newQuestion.indexOf('?')>-1){
answer.value='Thinking...'
try{
constres=awaitfetch('//yesno.wtf/api')
answer.value=(awaitres.json()).answer
}catch(error){
answer.value='Error!CouldnotreachtheAPI.'+error
}
}
})
</script>
<template>
<p>
Askayes/noquestion:
<inputv-model="question"/>
</p>
<p>{{answer}}</p>
</template>
偵聽數據源類型
watch的第一個參數可以是不同形式的“數據源”:它可以是一個ref(包括計算
屬性)、一個響應式對象、一個getter函數、或多個數據源組成的數組:
constx=ref(0)
consty=ref(0)
//單個ref
watch(x,(newX)=>{
console.log(`xis${newX}`)
})
//getter函數
watch(
()=>x.value+y.value,
(sum)=>{
console.log(`sumofx+yis:${sum}`)
}
)
//多個來源組成的數組
watch([x,()=>y.value],([newX,newY])=>{
console.log(`xis${newX}andyis${newY}`)
})
注意,你不能直接偵聽響應式對象的屬性值,例如:
constobj=reactive({count:0})
//錯誤,因為watch()得到的參數是一個number
watch(obj.count,(count)=>{
console.log(`countis:${count}`)
})
這里需要用一個返回該屬性的getter函數:
//提供一個getter函數
watch(
()=>obj.count,
(count)=>{
console.log(`countis:${count}`)
}
)
深層偵聽器
直接給watch()傳入一個響應式對象,會隱式地創建一個深層偵聽器——該回調函數在所有嵌套的變更時都會被觸發:
constobj=reactive({count:0})
watch(obj,(newValue,oldValue)=>{
//在嵌套的屬性變更時觸發
//注意:`newValue`此處和`oldValue`是相等的
//因為它們是同一個對象!
})
obj.count++
相比之下,一個返回響應式對象的getter函數,只有在返回不同的對象時,才會觸發回調:
watch(
()=>state.someObject,
()=>{
//僅當state.someObject被替換時觸發
}
)
你也可以給上面這個例子顯式地加上deep選項,強制轉成深層偵聽器:
watch(
()=>state.someObject,
(newValue,oldValue)=>{
//注意:`newValue`此處和`oldValue`是相等的
//*除非*state.someObject被整個替換了
},
{deep:true}
)
即時回調的偵聽器
watch默認是懶執行的:僅當數據源變化時,才會執行回調。但在某些場景中,我們希望在創建偵聽器時,立即執行一遍回調。舉例來說,我們想請求一些初始數據,然后在相關狀態更改時重新請求數據。我們可以通過傳入immediate:true選項來強制偵聽器的回調立即執行:
watch(source,(newValue,oldValue)=>{
//立即執行,且當`source`改變時再次執行
},{immediate:true})
二、兩者的區別
跟據兩個技術點的語法用途和設計原理主要有以下區別:
1.計算屬性有緩存機制,偵聽器沒有
2.計算屬性不支持異步操作,偵聽器支持
3.計算屬性可以給vue新增屬性,偵聽器必須是data中已有的屬性
4.計算屬性只要使用了就會立即執行一次,偵聽器默認只有當數據第一次改
變才會執行,需要設置immediate屬性來控制是否立即執行一次
5.計算屬性函數一定要有返回值,偵聽器不用寫返回值
6.計算屬性是解決模板語法冗余,而偵聽器是監聽某一個數據的變化

