React Diff算法與傳統的Diff算法理解

平時都是搞框架搞業務寫代碼,沒有靜下心來去深層次的看一些東西,最近公司人員變動,便承擔開始篩選簡歷面試,也開始讓自己梳理了偏底層基礎認知的內容。本章就來講下React的Diff算法和傳統Diff算法的不同及React的Diff算法都做了什麼,怎麼把時間複雜度從O(n^3)減少到O(n)?

Diff算法的作用:

用來計算出Virtual DOM中改變的部分,然後針對該部分進行原生DOM操作,而不用重新渲染整個頁面。

Diff算法三大策略:

  • 1.Tree Diff :Web UI中DOM節點跨層級的移動操作特別少,可以忽略不計
  • 2.Component Diff:擁有相同類的倆個組件將會生成相似的樹形結構,擁有不同類的倆個組件將會生成不同的樹形結構
  • 3.Element Diff:對於同一層級的一組子節點,它們可以通過唯一id進行區分

傳統的Diff算法:

計算兩顆樹形結構差異並進行轉換,循環遞歸每一個節點,其複雜度要達到O(n^3),其中n是節點總數,例如1000個節點就要進行數10億次的比較,效率十分低下。



比如左側樹每個節點與右側樹每個節點依次進行遍歷對比(A->A、A->D、A->B、A->C ......),時間複雜度O(n^2),查找對比差異後計算最小的轉換方式,最終時間複雜度爲O(n3)。
上面兩樹的變化而言,若要達到最小更新,首先要對比每個節點是否相同,即:

PA->LA
PA->LB
PA->LC
PA->LD
PB->LA
...

React的Diff算法:

React把傳統Diff的三個策略進行優化,將Diff複雜度從從O(n^3)降到了O(n)。

  • 1.Tree Diff優化:
    由於webUI中誇級移動操作非常少,可以忽略不計,React對Virtual DOM樹進行層級控制,只會對同級DOM節點進行比較,即同一個父元素下的所有子節點,當發現節點已經不存在了,則會刪除掉該節點下所有的子節點,不會再進行比較。這樣只需要對DOM樹進行一次遍歷,就可以完成整個樹的比較。複雜度變爲O(n)。
    疑問:當我們的DOM節點進行跨層級操作時,diff會有怎麼樣的表現呢?
    當出現節點跨層級移動時,並不會出現想象中的移動操作,而是會進行刪除,重新創建的動作,這是一種很影響React性能的操作。因此官方也不建議進行DOM節點跨層級的操作。


  • 2.Component Diff優化:
    擁有相同類型的兩個組件產生的DOM結構也是相似的,不同類型的兩個組件產生的DOM結構則不近相同。
    React是基於組件構建應用的,對於組件間的比較所採用的策略也是非常簡潔和高效的。
    如果是同一類型的組件,則按照原策略進行Virtual DOM比較。
    如果不是同一類型的組件,則將其判斷爲dirty component,從而替換整個組件下的所有子節點。
    如果是同一個類型的組件,有可能經過一輪Virtual DOM比較下,並沒有發生變化。如果我們能夠提前確切知道這一點,那麼就可以省下大量的Diff運算時間。因此,React允許用戶通過shouldComponentUpdate()來判斷該組件是否需要進行Diff算法分析。
    比如說當組件A變爲組件B時,即使這兩個組件結構相似,一旦React判斷A和B是不用類型的組件,就不會比較兩者的結構,而是直接刪除組件A,重新創建組件B及其子節點。雖然當兩個組件是不同類型但結構相似時,進行Diff算法分析會影響性能,但是畢竟不同類型的組件存在相似DOM樹的情況在實際開發過程中很少出現,因此這種極端因素很難在實際開發過程中造成重大影響。

  • 3.Element Diff優化:
    對於同一層級的一組子節點,通過分配唯一唯一id進行區分(key值)。
    當節點屬於同一層級時,diff提供了3種節點操作,分別爲INSERT_MARKUP(插入),MOVE_EXISTING(移動),REMOVE_NODE(刪除)。
    INSERT_MARKUP:新的組件類型不在舊集合中,即全新的節點,需要對新節點進行插入操作。
    MOVE_EXISTING:舊集合中有新組件類型,且element是可更新的類型,這時候就需要做移動操作,可以複用以前的DOM節點。
    REMOVE_NODE:舊組件類型,在新集合裏也有,但對應的element不同則不能直接複用和更新,需要執行刪除操作,或者舊組件不在新集合裏的,也需要執行刪除操作。

總結:

*1.React通過制定自己的diff策略,將 O(n3) 複雜度的問題轉換成 O(n) 複雜度的問題;
React是如何將O(n3) 複雜度的問題轉換成 O(n) :
(1).只進行同級比較。
(2).不同類的React組件會被當做完全不同的DOM結構而被完全替換。
(3).key :開發人員可以通過給Virtual DOM一個唯一的key屬性暗示React這是同一個DOM結構,反之若key值不同則會被當做完全不同的DOM結構。
2.React通過分層求異的策略,對Tree Diff進行算法優化;
3.React通過相同類生成相似樹形結構,不同類生成不同樹形結構的策略,對Component Diff進行算法優化。
4.React通過設置唯一key的策略,對Element Diff進行算法優化;
注意:
5.開發組件要保持穩定的DOM結構會有助於性能的提升;
6.儘量減少類似將最後一個節點移動到列表首部的操作,當節點數量過大或更新操作過於頻繁時,在一定程度上會影響React的渲染性能。
7.React的Diff算法是在render裏面進行計算的。

以上是自己的理解總結出的內容,如有錯誤地方,還望大家指正,相互探討。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章