聊一聊Diff算法

場景

計算兩顆樹形結構差別並進行轉換vue

文中, 所說起n均表明節點的個數node


傳統Diff算法

處理方案: 循環遞歸每個節點react

傳統diffweb

如上所示, 左側樹a節點依次進行以下對比:算法

a->e、a->d、a->b、a->c、a->adom

以後左側樹其它節點b、c、d、e亦是與右側樹每一個節點對比, 算法複雜度能達到O(n^2)函數

查找完差別後還需計算最小轉換方式,這其中的原理我沒仔細去看,最終達到的算法複雜度是O(n^3)性能

將兩顆樹中全部的節點一一對比須要O(n²)的複雜度,在對比過程當中發現舊節點在新的樹中未找到,那麼就須要把舊節點刪除,刪除一棵樹的一個節點(找到一個合適的節點放到被刪除的位置)的時間複雜度爲O(n),同理添加新節點的複雜度也是O(n),合起來diff兩個樹的複雜度就是O(n³)優化


優化的Diff算法

vue和react的虛擬DOM的diff算法大體相同,其核心是基於兩個簡單的假設:component

  1. 兩個相同的組件產生相似的DOM結構,不一樣的組件產生不一樣的DOM結構
  2. 同一層級的一組節點,他們能夠經過惟一的id進行區分

(優化的)diff三點策略:

  • web UI中DOM節點跨層級的移動操做特別少,能夠忽略不計。
  • 擁有相同類型的兩個組件將會生成類似的樹形結構,擁有不一樣類型的兩個組件將會生成不一樣樹形結構。
  • 對於同一層級的一組自節點,他們能夠經過惟一id進行區分。

即, 比較只會在同層級進行, 不會跨層級比較

React優化Diff算法

基於以上優化的diff三點策略,react分別進行如下算法優化

  • tree diff
  • component diff
  • element diff
tree diff

react對樹的算法進行了分層比較。react 經過 updateDepth對Virtual Dom樹進行層級控制,只會對相同顏色框內的節點進行比較,即同一個父節點下的全部子節點。當發現節點不存在,則該節點和其子節點都會被刪除。這樣是須要遍歷一次dom樹,就完成了整個dom樹的對比

分層比較 img

若是是跨層級的移動操做,如圖

跨層級操做 img

當根結點發現A消失了,會刪除掉A以及他的子節點。當發現D上多了一個A節點,會建立A(包括其子節點)節點做爲子節點

因此:當進行跨層級的移動操做,react並非簡單的進行移動,而是進行了刪除和建立的操做,這會影響到react性能。因此要儘可能避免跨層級的操做。(例如:控制display來達到顯示和隱藏,而不是真的添加和刪除dom)

component diff
  • 若是是同類型的組件,則直接對比virtual Dom tree
  • 若是不是同類型的組件,會直接替換掉組件下的全部子組件
  • 若是類型相同,可是可能virtual DOM 沒有變化,這種狀況下咱們可使用shouldComponentUpdate() 來判斷是否須要進行diff

component vs img

若是組件D和組件G,若是類型不一樣,可是結構相似。這種狀況下,由於類型不一樣,因此react會刪除D,建立G。因此咱們可使用shouldComponentUpdate()返回false不進行diff。

針對react15, 16出了新的生命週期

因此:component diff 主要是使用shouldComponentUpdate() 來進行優化

element diff

element diff 涉及三種操做:插入,移動,刪除

不使用key的狀況 img

不使用key的話,react對新老集合對比,發現新集合中B不等於老集合中的A,因而刪除了A,建立了B,依此類推直到刪除了老集合中的D,建立了C於新集合。=

醬紫會產生渲染性能瓶頸,因而react容許添加key進行區分

使用key的狀況 img

react首先對新集合進行遍歷,for( name in nextChildren),經過惟一key來判斷老集合中是否存在相同的節點,若是沒有的話建立,若是有的話,if (preChild === nextChild ) 進行移動操做

移動優化
在移動前,會將節點在新集合中的位置和在老集合中lastIndex進行比較,若是if (child._mountIndex < lastIndex) 進行移動操做,不然不進行移動操做。這是一種順序移動優化。只有在新集合的位置 小於 在老集合中的位置  才進行移動。

若是遍歷的過程當中,發如今新集合中沒有,可是在老集合中的節點,會進行刪除操做

因此:element diff 經過惟一key 進行diff 優化。

總結:
1.react中儘可能減小跨層級的操做。 2.可使用shouldComponentUpdate() 來避免react重複渲染。 3.添加惟一key,減小沒必要要的重渲染

Vue優化Diff

vue2.0加入了virtual dom,和react擁有相同的 diff 優化原則

差別就在於, diff的過程就是調用patch函數,就像打補丁同樣修改真實dom

  • patchVnode
  • updateChildren

updateChildren是vue diff的核心
過程能夠歸納爲:oldCh和newCh各有兩個頭尾的變量StartIdx和EndIdx,它們的2個變量相互比較,一共有4種比較方式。若是4種比較都沒匹配,若是設置了key,就會用key進行比較,在比較的過程當中,變量會往中間靠,一旦StartIdx>EndIdx代表oldCh和newCh至少有一個已經遍歷完了,就會結束比較

相關文章
相關標籤/搜索