在vue中,使用watch來(lái)響應(yīng)數(shù)據(jù)的變化。
一、 watch是什么?
監(jiān)測(cè) Vue 實(shí)例變化的一個(gè)表達(dá)式或方法?;卣{(diào)函數(shù)得到的參數(shù)為新值和舊值,用一個(gè)函數(shù)取代。
簡(jiǎn)潔的說(shuō):watch的作用可以監(jiān)控一個(gè)值的變換,并調(diào)用因?yàn)樽兓枰獔?zhí)行的方法??梢酝ㄟ^(guò)watch動(dòng)態(tài)改變關(guān)聯(lián)的狀態(tài)。
watch函數(shù)的參數(shù)中,第一個(gè)是改變之前的值,第二個(gè)是改變之后的值, 這兩個(gè)參數(shù)非常有用。
watch的2個(gè)參數(shù) (newValue, oldValue)
1.需要新舊對(duì)比時(shí) 可以用到第二個(gè)參數(shù)
2.參數(shù)不寫,直接通過(guò) this.數(shù)據(jù)的方式 也可以獲取到最新的數(shù)據(jù)
AcNo(val, oldVal) {
if(val != oldVal) {
const params = {
AcNo: this.AcNo
}
this.$http.post('/eweb/eweb-query.AcInfoQry.do', params).then(res => {
this.Balance = res.AvailBal
this.Currency = res.Currency
this.AcName = res.AcName
})
}
},
二、應(yīng)用場(chǎng)景
當(dāng)需要在數(shù)據(jù)變化時(shí)執(zhí)行異步或開(kāi)銷較大的操作時(shí),這個(gè)方式是最有用的。例如ajax請(qǐng)求,復(fù)雜的業(yè)務(wù)邏輯處理等。
三、應(yīng)用
<1>基礎(chǔ)用法
數(shù)據(jù)初始化
data() {
return {
a: 1,
b: 'enen',
shipStatusArr: {
name: 'zhangsanlisi',
age: 12
}
}
}
mounted
mounted () {
this.a = 2
this.shipStatusArr.name = 'lisi'
}
watch
(1)watch基本數(shù)據(jù)類型(string,number等)
watch: {
a: function (newValue, oldVal) {
console.log( newValue, oldVal )
}
// 也可以寫成
a(newValue, oldVal){
console.log( newValue, oldVal )
}
}
// handler方法和immediate屬性
// 上面的例子是值變化時(shí)候,watch才執(zhí)行,我們想讓值最初時(shí)候watch就執(zhí)行就用到了handler和immediate屬性
watch: {
a: {
handler(newName, oldName) {
this.fullName = newName + ' ' + this.lastName;
},
// 代表在wacth里聲明了a這個(gè)方法之后立即先去執(zhí)行handler方法,如果設(shè)置了false,那么效果和上邊例子一樣
immediate: true
}
}
(2)watch對(duì)象/數(shù)組(復(fù)雜數(shù)據(jù)類型)
# deep屬性
需要使用handler函數(shù),并且開(kāi)啟深度偵聽(tīng)deep
當(dāng)需要監(jiān)聽(tīng)一個(gè)對(duì)象的改變時(shí),普通的watch方法無(wú)法監(jiān)聽(tīng)到對(duì)象內(nèi)部屬性的改變,只有data中的數(shù)據(jù)才能夠監(jiān)聽(tīng)到變化,此時(shí)就需要deep屬性對(duì)對(duì)象進(jìn)行深度監(jiān)聽(tīng)。
當(dāng)監(jiān)測(cè)為對(duì)象的時(shí)候(非數(shù)組情況),deep = true 可以監(jiān)測(cè)對(duì)象中屬性的變化,并且(監(jiān)測(cè)為對(duì)象的時(shí)候,newVal == oldVal)
此時(shí),需要watch對(duì)象的某一具體屬性才可以監(jiān)聽(tīng)到數(shù)據(jù)的變化,具體做法是使用對(duì)象點(diǎn)方法獲取到屬性,并且用引號(hào)
(1)如果要觀察data下一個(gè)對(duì)象的屬性,可以使用 '對(duì)象.屬性' 的方式, 注意: 一定要要引號(hào)。
(2)如果改變了一個(gè)對(duì)象的屬性,就必須使用 deep: true, 否則檢測(cè)不到變化。
(3)數(shù)組(一維、多維)的變化不需要通過(guò)深度監(jiān)聽(tīng),對(duì)象數(shù)組中對(duì)象的屬性變化則需要deep深度監(jiān)聽(tīng)。
watch: {
shipStatusArr: {
handler(newValue, oldValue) {
console.log(newValue, oldValue)
},
deep: true // 深度監(jiān)聽(tīng)
}
}
// 下面的做法,檢測(cè)對(duì)象的時(shí)候, newVal == oldVal
watch: {
obj: {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true
}
}
// 延伸
受現(xiàn)代 JavaScript 的限制 (以及廢棄 Object.observe),Vue 不能檢測(cè)到對(duì)象屬性的添加或刪除。由于 Vue 會(huì)在初始化實(shí)例時(shí)對(duì)屬性執(zhí)行 getter/setter 轉(zhuǎn)化過(guò)程,所以屬性必須在 data 對(duì)象上存在才能讓 Vue 轉(zhuǎn)換它,這樣才能讓它是響應(yīng)的。
默認(rèn)情況下 handler 只監(jiān)聽(tīng)obj這個(gè)屬性它的引用的變化,我們只有給obj賦值的時(shí)候它才會(huì)監(jiān)聽(tīng)到,比如我們?cè)?mounted事件鉤子函數(shù)中對(duì)obj進(jìn)行重新賦值
如果此時(shí),需要對(duì)obj對(duì)象里的屬性a的值作監(jiān)測(cè),可以這樣處理,這時(shí)候deep屬性就派上用場(chǎng)了
watch: {
obj: {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
deep: true
}
}
這樣的方法對(duì)性能影響很大,修改obj里面任何一個(gè)屬性都會(huì)觸發(fā)這個(gè)監(jiān)聽(tīng)器里的 handler。我們可以做如下處理:
watch: {
'obj.a': {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
// deep: true
}
}
<2>進(jìn)階用法
(1)監(jiān)聽(tīng)對(duì)象的某一屬性
以上監(jiān)聽(tīng)shipStatusArr的用法,會(huì)在shipStatusArr中任一屬性變化時(shí)觸發(fā),如果是只監(jiān)聽(tīng)shipStatusArr的name變化,更優(yōu)寫法是:
watch: {
'shipStatusArr.name': function (newValue, oldVal) {
console.log( newValue, oldVal )
}
}
設(shè)置deep: true 則可以監(jiān)聽(tīng)到shipStatusArr.name的變化,此時(shí)會(huì)給shipStatusArr的所有屬性都加上這個(gè)監(jiān)聽(tīng)器,當(dāng)對(duì)象屬性較多時(shí),每個(gè)屬性值的變化都會(huì)執(zhí)行handler。如果只需要監(jiān)聽(tīng)對(duì)象中的一個(gè)屬性值,則可以做以下優(yōu)化:使用字符串的形式監(jiān)聽(tīng)對(duì)象屬性。這樣只會(huì)給對(duì)象的某個(gè)特定的屬性加監(jiān)聽(tīng)器。
(2)immediate屬性
進(jìn)入組件的時(shí)候,第一次并不會(huì)執(zhí)行watch,只有值發(fā)生改變才會(huì)執(zhí)行 , 是因?yàn)閕mmediate 默認(rèn) false
當(dāng) immediate = true 的時(shí)候,進(jìn)入組件會(huì)立即執(zhí)行。并且可以監(jiān)測(cè)到組件傳遞數(shù)據(jù)。
回調(diào)將會(huì)在偵聽(tīng)開(kāi)始之后被立即調(diào)用。
如果我們需要在最初綁定值的時(shí)候也執(zhí)行函數(shù),則就需要用到immediate屬性。
監(jiān)聽(tīng)的復(fù)雜數(shù)據(jù)后面寫成對(duì)象形式,包含handler方法和immediate,之前我們寫的函數(shù)其實(shí)就是在寫這個(gè)handler方法。
immediate表示在watch中首次綁定的時(shí)候,是否執(zhí)行handler,值為true則表示在watch中聲明的時(shí)候,就立即執(zhí)行handler方法,值為false,則和一般使用watch一樣,在數(shù)據(jù)發(fā)生變化的時(shí)候才執(zhí)行handler。
watch: {
shipStatusArr: {
handler(newValue, oldValue) {
console.log(newValue, oldValue)
},
deep: true,
immediate: true
}
}
四、注意問(wèn)題
(1)在watch中不要使用箭頭函數(shù),即不應(yīng)該使用箭頭函數(shù)來(lái)定義 watcher 函數(shù) , 因?yàn)榧^函數(shù)中的this是指向當(dāng)前作用域
(2)開(kāi)啟深度偵聽(tīng)后,觸發(fā)一次,兩個(gè)數(shù)據(jù)一致,這是vue做了處理
=>復(fù)雜數(shù)據(jù)類型的偵聽(tīng)需要開(kāi)啟深度偵聽(tīng)才可以檢測(cè)到內(nèi)部數(shù)據(jù)的改變,但開(kāi)啟深度偵聽(tīng)后,2個(gè)參數(shù)值是相同的,都是為數(shù)據(jù)的內(nèi)存地址,精確到對(duì)象的屬性名進(jìn)行深度偵聽(tīng)可以解決此問(wèn)題
=>如果修改復(fù)雜類型內(nèi)部的數(shù)據(jù)也會(huì)觸發(fā),觸發(fā)的頻率就回比較高,因?yàn)閺?fù)制類型數(shù)據(jù)可能多個(gè)地方使用,如果都會(huì)觸發(fā)偵聽(tīng)器,則會(huì)一直執(zhí)行,因此vue做了這個(gè)優(yōu)化,不發(fā)生觸發(fā),如果非要偵聽(tīng),可以使用深度偵聽(tīng)
五、工作中實(shí)際運(yùn)用
情景一
有時(shí)候,在使用watch偵聽(tīng)數(shù)據(jù)變化后 , 需要清空某些數(shù)據(jù) , 同時(shí)用計(jì)算屬性對(duì)該字段進(jìn)行判斷
如果直接用等于號(hào)賦值清空 , 計(jì)算屬性由于有緩存而導(dǎo)致 , 計(jì)算屬性認(rèn)為被清空的數(shù)據(jù)沒(méi)有變化 , 從而不會(huì)發(fā)生重新計(jì)算
需要使用$set清空數(shù)據(jù) , 并且讓被清空的字段是響應(yīng)式的
如下所示

可以給watch檢測(cè)的變量賦值
watch: {
Qmoney(newValue, oldValue){
if (newValue != oldValue){
this.Qmoney = newValue
this.RemainingSum = parseFloat(this.detail.BillBalance) - this.Qmoney
}
}
},
情景二
watch監(jiān)聽(tīng)的值可以不判斷條件,只要數(shù)據(jù)發(fā)生變化就執(zhí)行某些邏輯
比如 搜索功能實(shí)現(xiàn)
watch: {
search (val) {
console.log('watch---',val)
// if(val){ // 這里僅僅是判斷val有值的情況,但不涵蓋val為空,對(duì)于搜索功能,只考慮數(shù)據(jù)變化(包含數(shù)據(jù)為空的情況)執(zhí)行數(shù)據(jù)獲取接口即可,所以把if條件去掉即可
this.CurrentIndex = '0'
this.BankName = val
this.List3 = []
this.getData('watch')
// }
}
},