Vue原始碼學習(十二):列隊處理(防抖最佳化,多次呼叫,只處理一次)

養肥胖虎發表於2023-10-19

好傢伙,

 

本篇講的是資料更新請求列隊處理

 

1.一些效能問題

資料更新的核心方法是watcher.updata方法

實際上也就是vm._updata()方法,

vm._updata()方法中的patch()方法用於將新的虛擬DOM樹與舊的虛擬DOM樹進行比較,

並將差異更新到實際的DOM樹上.

這一步是非常消耗效能的

 

2."問題"例項

來寫一個多次更新的例子

 這裡我們可以看到,updata被觸發了三次,也就是 .patch()方法被觸發了三次

同樣的操作做了三次,顯然後兩次是多餘的

這顯然是可以最佳化的,我們來做些最佳化吧

 

3.最佳化

先講一下思路元件與watcher一一對應

1.當三個請求同時發出,我們只進行一次操作

2.將需要操作的watcher存到一個陣列中,在單次操作中呼叫更新方法

有點抽象

上程式碼:

class Watcher{
    /* 
    xxx
    */
    run(){
        this.getter()
    }
    updata() { //三次
        //注意:不要資料更新後每次都呼叫 get 方法 ,get 方法回重新渲染
        //快取
        // this.get() //重新渲染
        queueWatcher(this)
    }

}
let queue = [] // 將需要批次更新的watcher 存放到一個列隊中
let has = {}
let pending = false

function queueWatcher(watcher) {
    let id = watcher.id // 每個元件都是同一個 watcher
       console.log(id) //去重
    if (has[id] == null) {//去重
        //列隊處理
        queue.push(watcher)//將wacher 新增到列隊中
        has[id] = true
        //防抖 :使用者觸發多次,只觸發一個 非同步,同步
        if (!pending) {
            // 非同步:等待同步程式碼執行完畢之後,再執行
            setTimeout(()=>{
              queue.forEach(item=>item.run())
              queue = []
              has = {}
              pending = false
            },0)
        }
        pending = true
    }
}

此處,

 

a. 首先獲取到 watcher 的 id(假設每個元件都是同一個 watcher)。

b. 判斷佇列中是否已存在相同的 watcher,透過判斷 has 物件中是否存在該 id 來實現。

c. 如果佇列中不存在該 watcher,將其新增到佇列中,並將該 id 新增到 has 物件中,表示已存在。

d. 透過 setTimeout 將佇列中的所有 watcher 的 run 方法封裝成一個非同步任務,等待當前同步程式碼執行完畢後執行

e. 設定 pending 為 true,表示當前有一個非同步任務正在執行。

f.  執行setTimeout()中的程式碼

 

這樣第一次執行了if()塊,隨後的幾次操作中pending被設定為true後if()塊不再執行

同步任務完成後,執行非同步任務

 

這樣,透過非同步處理的方式實現了,觸發多次,只執行一次的效果

 

相關文章