Patch和Diff算法
網上看了好多的博客和源碼教程,感覺很多仔細的地方沒有說清,而且在一些複雜的部分加了好多的描述,所以就想自己也寫下心得, 方便自己, 方便他人,有興趣的同學可以關注我的github裏面有我之前一些博文 github/193Eric
我們知道的,在數據更改後,會觸發getter,然後通過dep.notify()來通知watcher觸發update進而更新視圖,最終是通過Diff算法來對比新老Vnode的差異,並把差異更新到Dom視圖上
Diff
我們知道的,Virtual DOM是一顆樹,而diff算法主要把兩顆樹進行對比,找出之間的差異,來渲染頁面
diff 算法是通過同層的樹節點進行比較而非對樹進行逐層搜索遍歷的方式,所以時間複雜度只有 O(n),是一種相當高效的算法
1.調用patch函數比較Vnode和OldVnode,如果不一樣直接return Vnode即將Vnode真實化後替換掉DOM中的節點
2.如果OldVnode和Vnode值得進一步比較則調用patchVnode方法進行進一步比較,分爲以下幾種情況:
-
Vnode有子節點,但是OldVnode沒有,則將Vnode的子節點真實化後添加到真實DOM上
-
Vnode沒有子節點,但是OldVnode上有,則將真實DOM上相應位置的節點刪除掉
-
Vnode和OldVnode都有文本節點但是內容不一樣,則將真實DOM上的文本節點替換爲Vnode上的文本節點
-
Vnode和OldVnode上都有子節點,需要調用updateChildren函數進一步比較
updateChildren比較規則
-
提取出Vnode和OldVnode中的子節點分別爲vCh和OldCh,並且提出各自的起始和結尾變量標記爲 oldS oldE newS newE
-
如果是oldS和newE匹配上了,那麼真實dom中的第一個節點會移到最後
-
如果是oldE和newS匹配上了,那麼真實dom中的最後一個節點會移到最前,匹配上的兩個指針向中間移動
-
如果都沒有,在沒有key的情況下直接在DOM的oldS位置的前面添加newS,同時newS+1。在有key的情況下會將newS和Oldch上的所有節點對比,如果有相同的則移動DOM並且將舊節點中這個位置置爲null且newS+1。如果還沒有則直接在DOM的oldS位置的前面添加newS且newS+1
直到出現任意一方的start>end,則有一方遍歷結束,整個比較也結束
updateChildren例子:
假設:
真實dom爲 A、B、C
oldDom爲 A1、B1、C1
newDom爲 A2、D2、C2、B2
先確定oldS = A1 ; oldE = C1; newS = A2; newE = B2
- 先對比oldS和newS,oldE和newE,發現oldS = newS 所以真實dom的A固定不動。排序爲 A、B、C
然後oldS = B1 ; oldE = C1; newS = D2; newE = B2
-
對比發現 oldS = newE , 所以真實dom,B要插入到後面去
-
真實dom排序爲:A、C、B
然後oldS = C1; oldE = C1; newS = D2; newE = B2
- 對比發現兩兩都不對等,所以走第三步。
- 假設有key存在,所以newS直接和oldDom裏面的所有節點對比,發現沒有存在,然後插入到oldS前面,且newS+1
- 真實dom排序爲:A、D、C、B
然後重新開始,oldS++ > oldE-- ,結束了。
這就是updateChildren,之後就是一直遍歷,運行updateChildren直到沒有