1. 程式人生 > 實用技巧 >vue響應式原理

vue響應式原理

vue響應式原理

一、響應式框架

  • 偵聽資料變化(資料攔截)
  • 收集誰依賴了資料,比如檢視、計算屬性等(訂閱更新)
  • 資料發生變化,通知依賴進行更新(派發更新)

操作DOM帶來的問題

  • 需要操作多個DOM元素,非常不方便
  • 每一次資料變化都要操作DOM
  • 頻繁操作DOM帶來的效能問題
  • 思路:不在邏輯計算過程中操作DOM,儘量減少DOM操作

MVVM模式

  • Android的訊息佇列模型
  • RxJava的響應式程式設計
  • AngularJS等

Vue不支援IE8及以下瀏覽器是由於低版本瀏覽器不支援defineProperty是ES5新方法

二、Vue如何實現響應式框架

關於物件

  1. 物件屬性包括資料屬性和訪問器屬性
  2. 資料屬性描述符:configurable、enumerable、writable、value
  3. 訪問器屬性描述符:configurable、enumerable、get、set
  4. 屬性描述符屬於內部特性,用於描述屬性行為,無法直接訪問,但可通過defineProperty進行修改
  5. 訪問器屬性沒有資料值,取而代之的是get、set函式
  6. 將資料屬性改為訪問器屬性,使用getter和setter來攔截資料的訪問和修改是響應式的基石

關於資料攔截

元件init階段,Vue的data屬性會被Observe類reactive化,即加上getter和setter函式,並建立依賴Dep

function observe(obj){
    if(!obj || typeof obj !== object){
        return
    }
    Object.keys(obj).forEach(key=>{
        defineReactive(obj,key,obj[key])
    })
    function defineReactive(obj,key,value){
        observe(value)
        Object.defineProperty(obj,key,{
            enumerable:true,
            configurable:true,
            get:function(){
                // 依賴收集
                return value
            },
            set:function(){
                observe(newValue)
                if(newValue !== value){
                    value = newValue
                    //派發更新
                }
            }
        })
    }
}

三、Vue如何保證UI更新?

物件變更檢測

  1. 由於javascript的限制,目前Vue無法檢測到物件屬性的新增或刪除(必須註冊到Vue的data中)
  2. 根級別的響應式屬性:不允許動態新增、刪除
  3. 巢狀物件的響應式屬性:允許新增、刪除,但是需要通過Vue提供的方法進行操作,通過Vue將屬性修改為響應式屬性
// 動態新增響應式屬性
Vue.set(vm.person,'age',18)
// 刪除響應式屬性
Vue.delete(vm.person,'name')
// 不能再根級別新增響應式屬性
Vue.set(vm,'count',12)

特殊的陣列的更新檢測

  1. 所有的物件上都有Object.defineProperty方法,可以通過get&set方法實現資料劫持,但是陣列沒有這兩個方法
  2. Vue將被偵聽的陣列的變異方法進行了包裹,使得他們也能觸發檢視更新
push、pop、shift、unshift、slice、sort、reverse
  1. 陣列替換也能通知到Vue,從而觸發檢視更新

Vue無法檢測到的陣列更新

  1. 通過索引直接修改陣列項
// 錯誤方法:
vm.items[index] = newVal
// 修正方法:
Vue.set(vm.items,index,newVal)  or vm.items.splice(index,1,newVal)
  1. 修改陣列的長度
// 錯誤方法:
vm.items.length
// 修正方法:
vm.items.splice(items.length)

巨集任務與微任務

  1. 巨集任務和微任務都是非同步任務
  2. 巨集任務和微任務都會被註冊到兩個不同的佇列中
  3. 巨集任務佇列不是一次性清空執行,而是執行一個巨集任務後,去清空執行一次微任務佇列
  4. 在執行微任務的過程中加入微任務佇列的微任務,也會在當前迴圈中執行
常見巨集任務:
setTimeout、setInterval、setImmediate、script、MessageChannel
常見微任務:
Promise、MutationObserver、Object.observe(廢棄)、process.nextTick(node)

DOM更新也是一個微任務

非同步重新整理機制

  1. 只要偵聽到資料變化,Vue將開啟一個佇列,並緩衝在同一事件迴圈中發生的所有資料變更。如果同一個watch被多次觸發,只會只會被推入到佇列中一次。這種在緩衝時去除重複資料對於避免不必要的計算和DOM操作是非常重要的。然後在下一個事件迴圈tic中,Vue重新整理佇列並執行實際(已去重)工作。
  2. Vue在內部對非同步佇列嘗試使用原生Promise.then、MutationObserver和setImmediate,如果執行環境不支援,則會採用setTimeout(fn,0)代替。

非同步佇列

  1. 所有同步任務都在主執行緒上執行,形成一個執行棧
  2. 主執行緒之外,還存在一個或多個任務佇列,主要非同步任務有了執行結果,就在非同步佇列中放置一個事件
  3. 一旦執行棧中的所有同步任務執行完畢,系統就會讀取“任務佇列”,看看裡面有哪些事件。那些對應的非同步任務,於是結束等待狀態,進入執行棧,開始執行。
  4. 主執行緒不斷重複上面第3步