通過同層的樹節點進行比較而非對樹進行逐層搜尋遍歷的方式,所以時間複雜度只有O(n),是一種相當高效的演算法
同層級只做三件事:增刪改。
具體規則是:
- new VNode不存在就刪;
- old VNode不存在就增;
- 都存在就 比較型別,型別不同直接替換、型別相同執行更新
兩個VNode型別相同,就執行更新操作,包括三種型別操作:屬性更新PROPS、文字更新TEXT、子節點更新REORDER
如果新舊VNode都是靜態的,那麼只需要替換elm以及componentInstance即可。
新老節點均有children子節點,則對子節點進行diff操作,呼叫updateChildren
如果老節點沒有子節點而新節點存在子節點,先清空老節點DOM的文字內容,然後為當前DOM節
點加入子節點。
當新節點沒有子節點而老節點有子節點的時候,則移除該DOM節點的所有子節點。
當新老節點都無子節點的時候,只是文字的替換。
⚠️ 下面詮釋順口溜開啟:src/core/vdom/patch.js檔案
一、開始遊標<=結束遊標
首先oldStartVnode、oldEndVnode
與newStartVnode、newEndVnode
兩兩交叉比較(1-4)
1. 舊頭對新頭,排著隊伍完後走
當
oldStartVnode
和newStartVnode
滿足sameVnode
,直接將該
VNode
節點進行patchVnode
即可,不需再遍歷就完成了一次迴圈 2. 舊尾對新尾,拉著尾巴順著走
當oldEndVnode
和newEndVnode
滿足sameVnode
,直接將該 VNode
節點進行patchVnode
即可,不需再遍歷就完成了一次迴圈 3. 舊頭對新尾,按著新尾排排隊
如果
oldStartVnode
與newEndVnode
滿足sameVnode
。說明oldStartVnode
已經跑到了oldEndVnode
後面去了,進行patchVnode
的同時還需要將真實DOM節點移動到oldEndVnode
的後面。 4. 舊尾對新頭,揪著舊尾前面走
如果oldEndVnode
與newStartVnode
滿足sameVnode
,說明oldEndVnode
跑到了oldStartVnode
的前面,進行patchVnode
的同時要將oldEndVnode
對應DOM移動到oldStartVnode
對應DOM的前面。 5. 綜上皆無果,老實迴圈接個來
如果以上情況均不符合,則在old VNode中找與
newStartVnode
滿足sameVnode
的vnodeToMove
,若
存在執行patchVnode,同時將vnodeToMove
對應DOM移動到oldStartVnode
對應的DOM的前面。
當然也有可能
newStartVnode
在old VNode節點中找不到一致的key,或者是即便key相同卻不是
sameVnode
,這個時候會呼叫createElm
建立一個新的DOM節點放到old VNode最前面。
二、老遊標先結束oldStartIdx > oldEndIdx批量新增
當結束時
oldStartIdx > oldEndIdx
,這個時候舊的VNode節點已經遍歷完了,但是新的節點還沒有。說
明瞭新的VNode節點實際上比老的VNode節點多,需要將剩下的VNode對應的DOM插入到真實DOM
中,此時呼叫addVnodes
(批量呼叫createElm介面)。 三、新遊標先結束newStartIdx > newEndIdx批量刪除
當結束時
newStartIdx > newEndIdx
時,說明新的VNode節點已經遍歷完了,但是老的節點還有
剩餘,需要從文件中刪 的節點刪除。