解析React Diff 算法

Diff 的做用

React Diff 會幫助咱們計算出 Virtual DOM 中真正發生變化的部分,而且只針對該部分進行實際的DOM操做,而不是對整個頁面進行從新渲染html

傳統diff算法的問題

傳統的diff算法是使用循環遞歸對節點進行依次對比,複雜度爲O(n^3),效率低下。react

React diff算法策略

  • 針對樹結構(tree diff):對UI層的DOM節點跨層級的操做進行忽略。(數量少)
  • 針對組件結構(component diff):擁有相同的兩個組件生成類似的樹形結構,擁有不一樣的兩個組件會生成不一樣的屬性結構。
  • 針對元素結構(element-diff): 對於同一層級的一組節點,使用具備惟一性的id區分 (key屬性)

tree diff 的特色

  • React 經過使用 updateDepth 對 虛擬DOM樹進行層次遍歷算法

  • 兩棵樹只對同一層級節點進行比較,只要該節點不存在了,那麼該節點與其全部子節點會被徹底刪除,不在進行進一步比較。性能

  • 只須要遍歷一次,便完成對整個DOM樹的比較。優化

React diff 只考慮同層次節點位置變換,若爲跨層級的位置變化,則是建立節點和刪除節點的操做。即在新位置上從新建立相同的節點,而刪除原位置的節點。3d

Tips: React 官方建議不要進行DOM節點的跨層級操做,但是經過CSS來隱藏,顯示節點,而不是真正地刪除和添加DOM節點,保持穩定的DOM結構會對性能提高有幫助。code

component diff的特色

  • 同一類型的組件,按照原策略(tree diff)比較 virtual DOM tree
  • 同類型組件,組件A轉化爲了組件B,若是virtual DOM 無變化,能夠經過shouldComponentUpdate()方法來判斷是否

React官方文檔對於 shouldComponentUpdate的介紹component

  • 不一樣類型的組件,那麼diff算法會把要改變的組件判斷爲dirty component,從而替換整個組件的全部節點。

就算結構再類似的組件,只要 React 判斷是不一樣的組件,就不會判斷是否爲不一樣類型的組件,就不會比較其結構,而是刪除組件以及其子組件,並建立新的組件以及其子節點。orm

element diff特色

對於處於同一層級的節點,React diff 提供了三種節點操做: 插入,移動,刪除cdn

  • 插入: 新的組件不在原來的集合中,而是全新的節點,則對集合進行插入操做。
  • 刪除:組件已經在集合中,但集合已經更新,此時節點就須要刪除。
  • 移動:組件已經存在於集合中,而且集合更新時,組件並無發生更新,只是位置發生改變,例如:(A,B,C,D) → (A,D,B,C), 若是爲傳統diff則會在檢測到舊集合中第二位爲B,新集合第二位爲D時刪除B,插入D,而且後面的全部節點都要從新加載,而 React diff 則是經過向同一層的節點添加 惟一key 進行區分,而且移動。

一些移動的場景與邏輯

節點相同,位置不一樣

nT73TS.md.jpg

按新集合中順序開始遍歷

  1. B在新集合中 lastIndex(相似浮標) = 0, 在舊集合中 index = 1,index > lastIndex 就認爲 B 對於集合中其餘元素位置無影響,不進行移動,以後lastIndex = max(index, lastIndex) = 1
  2. A在舊集合中 index = 0, 此時 lastIndex = 1, 知足 index < lastIndex, 則對A進行移動操做,此時lastIndex = max(Index, lastIndex) = 1
  3. D和B操做相同,同(1),不進行移動,此時lastIndex=max(index, lastIndex) = 3
  4. C和A操做相同,同(2),進行移動,此時lastIndex = max(index, lastIndex) = 3
節點位置均有變化

nT7GFg.md.jpg

1.同上面那種情形,B不進行移動,lastIndex=1

2.新集合中取得E,發現舊中不存在E,在 lastIndex處建立E,lastIndex++

3.在舊集合中取到C,C不移動,lastIndex=2

4.在舊集合中取到A,A移動到新集合中的位置,lastIndex=2

5.完成新集合中全部節點diff後,對舊集合進行循環遍歷,尋找新集合中不存在但就集合中的節點(此例中爲D),刪除D節點。

React diff 的不足之處

nT71w8.md.jpg

此例中D直接從最後一位提高至第一位,致使lastIndex在第一步直接提高爲3,使ABC在進行index與lastIndex的判斷時均處於 index < lastIndex 的狀況,使ABC都須要作移動操做。因此咱們應該減小將最後一個節點提高至第一個的操做,若是操做頻率較大或者節點數量較多時,會對渲染性能產生影響。

小結

  • React diff 與 傳統diff 的不一樣是 React經過優化將O(n^3)提高至O(n)
  • React 經過三個方面對tree diff, component diff, element diff 進行了優化
  • 在開發時,儘可能保持穩定的DOM結構,而且減小將最後的節點移動到首部的操做,可以優化渲染性能。

參考資料

《深刻React技術棧》

React 源碼剖析系列 - 難以想象的 react diff

相關文章
相關標籤/搜索