computed
和 watch
在 Vue
項目中可以說是非常常見慧瘤,這兩個方法看似都能實現(xiàn)對數(shù)據(jù)的監(jiān)聽咙好,那么兩者之間有什么區(qū)別呢仗哨?
computed 計算屬性
計算屬性基于 data
中聲明過或者父組件傳遞的 props
中的數(shù)據(jù)通過計算得到的一個新值盗迟,這個新值只會根據(jù)已知值的變化而變化娩缰。通俗來講就是:這個屬性依賴其他屬性脂新,由其他屬性計算而來挪捕。 得出使用 computed
的常用場景為:
如果一個屬性是由其他屬性計算而來的,這個屬性依賴其他屬性争便,是一個多對一或者一對一级零,一般用
computed
。
先來看一看 computed
最常用的場景栗子:
<p>姓名:{{ fullName }}</p>
<script>
export default {
data() {
return {
firstName: '張',
lastName: '三'
}
},
computed: {
fullName() {
return this.firstName + this.lastName
}
}
};
</script>
在 computed
屬性對象中定義計算屬性的方法滞乙,和取 data
對象里的數(shù)據(jù)屬性一樣以屬性訪問的形式調(diào)用奏纪,即在頁面中使用 {{ 方法名 }}
來顯示計算的結(jié)果。
如上栗子中我們在 computed
中使用了 fullName
方法斩启,如果此時我們在 data
中也聲明一個屬性 fullName
序调,會報錯 Duplicated key 'fullName'
。因為 data
中的 fullName
屬性和 computed
中的 fullName
方法存在重復(fù)兔簇。如下栗子:
// 錯誤用法
data() {
return {
firstName: '張',
lastName: '三',
fullName: '張三'
}
},
computed: {
fullName() {
return this.firstName + this.lastName
}
}
在 Vue
的官方文檔中发绢,還特意強調(diào)了 computed
的一個重要特點硬耍,就是它具有緩存功能,我們先來看個栗子:
<p>姓名:{{ fullName }}</p>
<p>姓名:{{ fullName }}</p>
<p>姓名:{{ fullName }}</p>
<p>姓名:{{ fullName }}</p>
<p>姓名:{{ fullName }}</p>
<script>
export default {
data() {
return {
firstName: '張',
lastName: '三'
}
},
computed: {
fullName() {
console.log('computed') // 控制臺只打印了一次
return this.firstName + this.lastName
}
}
};
</script>
我們在頁面上多次顯示 fullName
朴摊,實際上這個方法只執(zhí)行了一次默垄,所以此處又可得出結(jié)論:computed
中的內(nèi)部方法在重復(fù)的調(diào)用中,只要依賴數(shù)據(jù)不變甚纲,直接取緩存中的計算結(jié)果口锭。只有依賴型數(shù)據(jù)發(fā)生改變,computed 才會重新計算介杆。
在 computed
中鹃操,所有的屬性都有一個 get()
和一個 set()
,默認一般走 get()
春哨,但是如果我們直接修改 fullName
時荆隘,則會調(diào)用 set()
方法。我們將上述代碼使用 get()
和 set()
進行拆分赴背,得到如下栗子:
<p>姓名:{{ fullName }}</p>
<button @click="handleClick">更改姓名</button>
<script>
export default {
data() {
return {
firstName: '張',
lastName: '三'
}
},
computed: {
fullName: {
get() {
return this.firstName + this.lastName
},
set(val) {
console.log(val) // 李四
}
}
}
methods: {
handleClick() {
this.fullName = '李四'
}
}
};
</script>
同時 computed
中不支持異步椰拒,當(dāng)computed內(nèi)有異步操作時會報錯,這里用 setTimeout
演示栗子如下:
computed: {
fullName() {
setTimeout(() => { // 報錯
return this.firstName + this.lastName
}, 30)
}
}
所以綜上總結(jié) computed
的一些特點如下:
- 支持緩存凰荚,只有依賴數(shù)據(jù)發(fā)生改變燃观,才會重新進行計算
- 不支持異步,當(dāng)
computed
內(nèi)有異步操作時無效 - 如果一個屬性是由其他屬性計算而來的便瑟,這個屬性依賴其他屬性缆毁,是一個多對一或者一對一,一般用
computed
- 如果
computed
屬性屬性值是函數(shù)到涂,那么默認會走get()
脊框;函數(shù)的返回值就是屬性的屬性值;在computed
中的践啄,屬性都有一個get()
和一個set()
浇雹,當(dāng)數(shù)據(jù)變化時,調(diào)用set()
屿讽。
watch 監(jiān)聽屬性
通過 vm
對象的 $watch()
或 watch
配置來監(jiān)聽 Vue
實例上的屬性變化箫爷,或某些特定數(shù)據(jù)的變化,然后執(zhí)行某些具體的業(yè)務(wù)邏輯和操作聂儒。當(dāng)屬性變化時虎锚,回調(diào)函數(shù)自動調(diào)用,在函數(shù)內(nèi)部進行計算衩婚。其可以監(jiān)聽的數(shù)據(jù)來源:data
窜护,props
,computed
內(nèi)的數(shù)據(jù)非春。
監(jiān)聽函數(shù)有兩個參數(shù)柱徙,第一個參數(shù)是最新的值缓屠,第二個參數(shù)是輸入之前的值,順序一定是先新值再舊值护侮,如果只寫一個參數(shù)敌完,那就是最新屬性值。
在使用時選擇 watch
還是 computed
羊初,還有一個參考點就是官網(wǎng)說的:當(dāng)需要在數(shù)據(jù)變化時執(zhí)行異步或開銷較大的操作時滨溉,watch
方式是最有用的。所以 watch
一定是支持異步的长赞。
watch
默認的是淺監(jiān)聽晦攒,(淺監(jiān)聽指只能監(jiān)聽到基礎(chǔ)類型的值變化情況),無法進行深度監(jiān)聽(深度監(jiān)聽指比如引用類型的值變化就無法正確監(jiān)聽)得哆。直接看個栗子吧:
<div>
<input type="text" v-model="name" />
<input type="text" v-model="info.city" />
<button @click="handleClickName">更改姓名</button>
<button @click="handleClickInfo">更改信息</button>
</div>
<script>
export default {
data() {
return {
name: '哪吒',
info: {
city: '北京'
}
}
},
watch: {
name(newVal, oldVal) {
// 值類型脯颜,可正常拿到
console.log('watch name', newVal, oldVal) // 姜子牙,哪吒
},
info(newVal, oldVal) {
// 引用類型贩据,拿不到
console.log('watch info', newVal, oldVal) // 控制臺無輸出結(jié)果
},
info: {
handler(newVal, oldVal) {
console.log('watch info', newVal, oldVal)
},
deep: true // 深度監(jiān)聽
}
},
methods: {
handleClickName() {
this.name = '姜子牙'
},
handleClickInfo() {
this.info.city = '上海'
}
}
}
</script>
官方告訴我們使用 watch
監(jiān)聽復(fù)雜數(shù)據(jù)類型就需要用到深度監(jiān)聽 deep
栋操。
-
deep
:為了發(fā)現(xiàn)對象內(nèi)部值的變化,可以在選項參數(shù)中指定 deep: true饱亮。注意監(jiān)聽數(shù)組的變更不需要這么做讼庇。
我們針對上述代碼使用 deep
進行重寫:
watch: {
info: {
handler(newVal, oldVal) {
console.log('watch info', newVal, oldVal) // {__ob__: Observer}
},
deep: true // 深度監(jiān)聽
}
}
得到打印結(jié)果如下:
好吧!并沒有得到我們的預(yù)期近尚,打印出來的
newVal
和 oldVal
值是一樣的,所以深度監(jiān)聽雖然可以監(jiān)聽到對象的變化场勤,但是無法監(jiān)聽到對象里面哪個具體屬性的變化戈锻。這是因為它們的引用指向同一個對象/數(shù)組。Vue
不會保留變更之前值的副本和媳。 vm.$watch 深度監(jiān)聽
若果要監(jiān)聽對象的單個屬性的變化格遭,有兩種方法:
- 直接監(jiān)聽對象的屬性
watch: {
'info.city'(newVal, oldVal) {
console.log('watch info', newVal, oldVal) // watch info 上海 北京
}
}
- 與
computed
屬性配合使用,computed
返回想要監(jiān)聽的屬性值留瞳,watch
用來監(jiān)聽
computed: {
changeCity() {
return this.info.city
}
},
watch: {
changeCity(newVal, oldVal) {
console.log('watch info', newVal, oldVal) // watch info 上海 北京
}
}
上面的代碼都是我們定義了一個按鈕拒迅,通過按鈕手動修改值來觸發(fā)值的變化進而使用 watch
進行監(jiān)聽,那么我們能不能在頁面初始化的時候就直接使用 watch
呢她倘?認識 handler
方法和 immediate
屬性璧微。
<p>姓名:{{ fullName }}</p> // 張三
<script>
data() {
return {
firstName: '張',
lastName: '三',
fullName: ''
}
},
watch: {
fullName: {
handler() {
this.fullName = this.firstName + this.lastName
},
immediate: true // 設(shè)置為 true,理解執(zhí)行 handle 方法
}
}
</script>
在 computed
中我們知道每一個屬性其實都有 get()
和 set()
硬梁,在 watch
中每一個屬性其實都有 handle()
和 imediate
屬性(默認為 false
)前硫。watch
的特點是最初綁定的時候是不會執(zhí)行的,而 immediate:true
代表如果在 wacth
里聲明了荧止,那么就會立即先去執(zhí)行里面的 handler
方法屹电。并且如果我們在 computed
中如果定義了 fullName
那么在 data
里就不能重復(fù)定義 fullName
阶剑,而 watch
則不受影響。
日常開發(fā)中危号,我們還可以使用 watch
來監(jiān)聽路由的變化牧愁,如下栗子:
watch: {
'$route'(to,from){
console.log(to); //to表示去往的界面
console.log(from); //from表示來自于哪個界面
if(to.path=="/shop/detail"){
console.log("商品詳情");
}
}
},
總結(jié):
watch
和computed
都是以Vue
的依賴追蹤機制為基礎(chǔ)的,當(dāng)某一個依賴型數(shù)據(jù)發(fā)生變化的時候外莲,所有依賴這個數(shù)據(jù)的相關(guān)數(shù)據(jù)會自動發(fā)生變化猪半,即自動調(diào)用相關(guān)的函數(shù),來實現(xiàn)數(shù)據(jù)的變動苍狰。當(dāng)依賴的值變化時办龄,在watch
中,是可以做一些復(fù)雜的操作的淋昭,而computed
中的依賴俐填,僅僅是一個值依賴于另一個值,是值上的依賴翔忽。
兩者常用應(yīng)用場景區(qū)分:
computed
:用于處理復(fù)雜的邏輯運算英融;一個數(shù)據(jù)受一個或多個數(shù)據(jù)影響;用來處理watch
和methods
無法處理的歇式,或處理起來不方便的情況驶悟。例如處理模板中的復(fù)雜表達式、購物車?yán)锩娴纳唐窋?shù)量和總金額之間的變化關(guān)系等材失。
watch
:用來處理當(dāng)一個屬性發(fā)生變化時痕鳍,需要執(zhí)行某些具體的業(yè)務(wù)邏輯操作,或要在數(shù)據(jù)變化時執(zhí)行異步或開銷較大的操作龙巨;一個數(shù)據(jù)改變影響多個數(shù)據(jù)笼呆。例如用來監(jiān)控路由、inpurt
輸入框值的特殊處理等旨别。
兩者主要的區(qū)別:
computed
- 初始化顯示或者相關(guān)的
data
诗赌、props
等屬性數(shù)據(jù)發(fā)生變化的時候調(diào)用 -
computed
不支持異步; - 計算屬性不在
data
中秸弛,它是基于data
或props
中的數(shù)據(jù)通過計算得到的一個新值铭若,這個新值根據(jù)已知值的變化而變化; - 在
computed
屬性對象中定義計算屬性的方法递览,和取data
對象里的數(shù)據(jù)屬性一樣叼屠,以屬性訪問的形式調(diào)用,但是computed
中定義的屬性名稱不能和data
中的屬性名稱重合绞铃; - 如果
computed
屬性值是函數(shù)环鲤,那么默認會走get
方法,必須要有一個返回值憎兽,函數(shù)的返回值就是屬性的屬性值冷离; -
computed
屬性值默認會緩存計算結(jié)果吵冒,在重復(fù)的調(diào)用中,只要依賴數(shù)據(jù)不變西剥,直接取緩存中的計算結(jié)果痹栖,只有依賴型數(shù)據(jù)發(fā)生改變伙单,computed
才會重新計算然眼; - 在
computed
中的,屬性都有一個get
和一個set
方法仔夺,當(dāng)數(shù)據(jù)變化時咆畏,調(diào)用set
方法南捂。
watch
- 主要用來監(jiān)聽某些特定數(shù)據(jù)的變化,從而進行某些具體的業(yè)務(wù)邏輯操作旧找,可以看作是
computed
和methods
的結(jié)合體溺健; - 可以監(jiān)聽的數(shù)據(jù)來源:
data
,props
钮蛛,computed
內(nèi)的數(shù)據(jù)鞭缭; -
watch
支持異步; - 不支持緩存魏颓,監(jiān)聽的數(shù)據(jù)改變岭辣,直接會觸發(fā)相應(yīng)的操作;
- 監(jiān)聽函數(shù)有兩個參數(shù)甸饱,第一個參數(shù)是最新的值沦童,第二個參數(shù)是輸入之前的值,順序一定是新值叹话,舊值偷遗。
如果文中有不對的地方或者理解有誤的地方歡迎大家提出并指正。每一天都要相對前一天進步一點渣刷,加油!4V颉辅柴!