在虛擬dom中diff的實現。
分別從3個方面:
2.在VUE2中的實現
vue 版本2.6.11
必要性分析:
vue 1中有細粒度的數據變化偵測,不需要虛擬DOM的,但是細粒度造成了大量開銷,只適合中小型項目。 vue 2中為了降低Watcher粒度,每個組件只有一個Watcher實例,狀態變化時只能通知到組件,只有引入diff才能精確找到發生變化的地方。 vue 2的diff算法,位於patch.js文件中,該算法來源於snabbdom。
在vue2中的執行方式:
diff過程整體遵循深度優先、同層比較的策略。 當數據發生改變時,會調用Dep.notify通知所有Watcher,調用patch給真實的DOM打補丁,更新相應的視圖。 patch兩個節點之間比較會根據它們是否擁有子節點或者文本節點做不同操作。 比較兩組子節點是重點:首先假設頭尾節點可能相同,做4次比對嘗試,如果沒有找到相同節點則遍歷查找,查找結束再按情況處理剩下的節點。 借助key通常可以非常精確找到相同節點。
patch:比對兩個虛擬dom時會有三種操作:刪除、新增、更新
patchVnode:比較兩個VNode三種類型操作 屬性更新、文本更新、子節點更新
updateChildren:用一種較高效的方式對比新舊兩個VNode的children,得出最小操作補丁。雙循環執行方式。
接下來對於updateChildren做一個圖解分析
在新老兩組VNode節點的頭尾兩側都有一個變量標記,在遍歷過程中這幾個變量都會向中間靠攏。 當oldStartIdx > oldEndIdx或者newStartIdx > newEndIdx時結束循環
循環開始
當 oldStartVnode和newStartVnode 滿足sameVnode,直接將該 VNode節點進行patchVnode
當 oldEndVnode和newEndVnode 滿足sameVnode,直接將該 VNode節點進行patchVnode
如果oldStartVnode與newEndVnode滿足sameVnode。說明oldStartVnode已經到oldEndVnode 后面,進行patchVnode的同時還需要將真實DOM節點移動到oldEndVnode后面。
如果oldEndVnode與newStartVnode滿足sameVnode,說明oldEndVnode到oldStartVnode前面,進行patchVnode的同時要將oldEndVnode對應DOM移動到oldStartVnode對應DOM的前面。
如果以上情況均不符合,則在oldVNode中找與newStartVnode相同的節點,若存在執行patchVnode,同時將elmToMove移動到oldStartIdx對應的DOM的前面。
當然也有可能newStartVnode在oldVNode節點中找不到一致的sameVnode,這個時候會調用 createElm創建一個新的DOM節點。
循環結束,處理剩下的節點。
當oldStartIdx > oldEndIdx,這個時候舊的VNode節點已經遍歷完了,但是新的節點還沒有。新的VNode節點實際上比老的VNode節點多,需要將剩下的VNode對應的DOM根據下標插入到真實DOM 中。
當結束時newStartIdx > newEndIdx時,說明新的VNode節點已經遍歷完了,但是老的節點還有剩余,需要在真實dom中,將區間為[oldStartIdx, oldEndIdx]的多余節點刪掉。