1. 程式人生 > 實用技巧 >vue watch監聽不到物件,探究 watch 原理

vue watch監聽不到物件,探究 watch 原理

最近使用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建立後會立刻執行回撥,這點不需要過多的介紹。