Vue深刻響應式原理

Vue最獨特的特性之一,非侵入性的響應式系統。vue

如何追蹤變化

當把一個普通的js對象傳入Vue實例做爲data選項,Vue將遍歷此對象的全部屬性,並使用Object.defineProperty把這些屬性所有轉爲getter/sertter。瀏覽器

Object.defineProperty是ES5的特性,因此Vue不支持IE8及如下版本的瀏覽器。app

這些getter/setter對用戶來講不可見,可是內部卻讓Vue可以追蹤依賴,在屬性被訪問和修改時通知變動。dom

不一樣瀏覽器在控制檯打印數據對象時對getter/setter的格式化不一樣,因此建議用vue-devtools來更好的獲取數據。異步

每個組件都對應一個watcher實例,它會在組件渲染的過程當中,把接觸過的數據屬性記錄爲依賴。以後當依賴項的setter觸發時,會通知wathcher,使它關聯的組件從新渲染。async

檢測變化的注意事項

由於Object.observer已經被廢棄,Vue沒法檢測到對象屬性的添加或刪除。 因爲Vue會在初始化實例時,對屬性執行getter/setter轉化,因此屬性必須在data對象上存在,才能讓Vue將它轉換爲響應式的。函數

var vm = new Vue({
    data:{
        a:1
    }
})
//vm.a是響應式的

vm.b = 2
//vm.b非響應式
複製代碼

對於已經建立的實例,Vue不容許動態添加跟級別的響應式屬性,可是可使用Vue.set(object,properName,value)方法向嵌套對象添加響應式屬性。ui

Vue.set(vm.someObject,'b',2)
複製代碼

還可使用vm.$set實例方法,也是全局Vue.set方法的別名:this

this.$set(this.someObject,'b',2)
複製代碼

有時候可能須要爲已有對象賦值多個新屬性,好比使用Objct.assign()或_.extend()。spa

可是這樣添加到對象上的新屬性不會觸發更新。這時候應該用源對象與要混合進去的對象的屬性一塊兒建立一個新的對象。

//代替Object.assign(this.someObject,{a:1,b:2})
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 });
複製代碼

聲明響應式屬性

因爲Vue不容許動態添加根級響應式屬性,因此必須在初始化實例前,聲明全部根級響應式屬性,哪怕只是一個空值:

var vm = new Vue({
    data:{
        message:''
    },
    template:'<div>{{ message }}</div>'
})

//以後設置message
vm.message = 'hello'
複製代碼

若是沒有在data中聲明message,Vue將警告渲染函數正在試圖訪問不存在的屬性。

這樣的限制

  • 消除了在依賴項跟蹤系統的一類邊界狀況
  • 使得Vue實例能更好配合類型檢查系統工做

可是這樣代碼可維護性有考慮:data 對象就像組件狀態的結構 (schema)。

異步更新隊列

Vue在更新DOM時是異步執行的,只要監聽到數據變化,Vue將開啓一個隊列,並緩衝在同一事件循環中發生的全部數據變動。

若是同一個watcher被屢次觸發,只會被推入到隊列一次。

這種在緩衝時去除重複數據對於避免沒必要要的計算和dom操做是很重要的。

而後在下一個事件循環'tick'中,Vue刷新隊列並執行實際,已去重的工做。

Vue在內部對異步隊列嘗試使用原生的Promise.then、MutationObserver和setImmediate,若是執行環節不支持,就採用setTimeout代替。

例如,當設置vm.someData = 'new value',該組件不會當即從新渲染。當刷新隊列時,組件會在下一個事件循環tick中更新。

雖然避免接觸DOM可是必須這樣作。

爲了在數據變化後等待Vue更新DOM,能夠在數據變化以後當即使用Vue.nextTick(callback)。這樣回調函數將在DOM更新完成後被調用。

var vm = new Vue({
    el:'#app',
    data:{
        message:'123'
    }
})

vm.message = 'new message'
vm.$el.textContent === 'new message' //false 
Vue.nextTick(function(){
    vm.$el.textContent === 'new message' //ture
})
複製代碼

在組件內使用vm.$nextTick()實例方法特別方便,由於它不須要全局Vue,而且回調函數中的this會自動綁定到Vue實例上:

Vue.component('example', {
    template: '<span>{{message}}</span>',
    data: function () {
        return {
            message: '未更新'
        };
    },
    methods: {
        updateMessage() {
            this.message = '已更新'
            console.log(this.$el.textContent);//未更新
            this.$nextTick(() => {
                console.log(this.$el.textContent);//已更新
            })
        }
    }
});
複製代碼

由於$nextTick()返回一個Promise對象,因此你可使用新的async/await語法完成相同的事

methods: {
        async updateMessage() {
            this.message = '已更新'
            console.log(this.$el.textContent);//未更新
            await this.$nextTick()
            console.log(this.$el.textContent);//已更新
        }
    }
複製代碼
相關文章
相關標籤/搜索