最近使用vue watch時,在某些模塊監聽不到對象的改變,無法觸發回調函數。
解決:
使用watch監聽對象時,只能監聽到該對象初始化時已存在的key值。
如下例監聽user對象,在初始化時沒有age屬性,那在mounted中給user.age賦值后不會觸發watch中的回調:
var app = new Vue({
el: '#app', data: { user: { name: 'zhangsan' } }, mounted() { var _this = this setTimeout(() => { _this.user.age = 10 },2000) }, watch: { user: { handler (val) { console.log('user update') }, deep:true } } })
若想監聽到這個變化,需要給user初始化的age:
data: {
user: {
name: 'zhangsan', age: 1 } },
問題原因及vue watch原理:
這個問題其實是比較低級的錯誤,說明對vue的設計原理和理念還是理解太淺。watch的基本用法這里不再贅述,下面通過遇到的這個問題,探究一下watch對object類型進行監聽的用法和原理。
通過官方文檔我們知道,當監聽的目標用對象進行配置時,共有三個配置屬性:
watch: {
a:{
handler: function (val, oldVal) { /* ... */ },
immediate:true,
deep: true
}
}
首先,handler是vue中定義好的屬性名,在用對象配置時,回調函數只能寫在handler中。如下圖源碼所示,在creatWatcher時,會驗證傳入的配置項handler是否為一個純對象類型,如果是對象類型,則會取handler.handler作為真正的回調函數;而下面的代碼則是只傳遞方法名或方法實體的處理邏輯。
關於deep屬性,大家都知道vue的綁定原理是建立在Object.defineProperty的set和get方法上的。給某個屬性綁定set和get之后,只有改變該屬性本身時,才會觸發set和get的回調函數,而改變其內部屬性時不會觸發。所以在vue內部創建watcher時會有一個traverse方法,當配置deep為true時,調用traverse遍歷其下每一個子屬性。如下源代碼:
而我們又知道vue只有在組件初始化時,會對data中數據進行set和get的綁定。這也是為什么在后續的業務邏輯中,給某個對象插入新屬性時不會觸發watch監聽的原因。
immediate屬性為true時,watcher創建后會立刻執行回調,這點不需要過多的介紹。