好傢伙,
本篇講的是資料更新請求列隊處理
資料更新的核心方法是watcher.updata方法
實際上也就是vm._updata()方法,
vm._updata()方法中的patch()方法用於將新的虛擬DOM樹與舊的虛擬DOM樹進行比較,
並將差異更新到實際的DOM樹上.
這一步是非常消耗效能的
來寫一個多次更新的例子
這裡我們可以看到,updata被觸發了三次,也就是 .patch()方法被觸發了三次
同樣的操作做了三次,顯然後兩次是多餘的
這顯然是可以優化的,我們來做些優化吧
先講一下思路元件與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()塊不再執行
同步任務完成後,執行非同步任務
這樣,通過非同步處理的方式實現了,觸發多次,只執行一次的效果