一圖看懂React diff

在這裏插入圖片描述
React 中最值得稱道的部分莫過於 Virtual DOM 與 diff 的完美結合,特別是其高效的 diff 算法,讓用戶可以無需顧忌性能問題而”任性自由”的刷新頁面,讓開發者也可以無需關心 Virtual DOM 背後的運作原理,因爲 React diff 會幫助我們計算出 Virtual DOM 中真正變化的部分,並只針對該部分進行實際 DOM 操作,而非重新渲染整個頁面,從而保證了每次操作更新後頁面的高效渲染,因此 Virtual DOM 與 diff 是保證 React 性能口碑的幕後推手。

1 diff 策略

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

以上三個策略,React 分別對 tree diff、component diff 以及 element diff 進行算法優化,事實也證明這三個前提策略是合理且準確的,它保證了整體界面構建的性能。

2 tree diff

基於策略一,React 對樹的算法進行了簡潔明瞭的優化,即對樹進行分層比較,兩棵樹只會對同一層次的節點進行比較。

既然 DOM 節點跨層級的移動操作少到可以忽略不計,針對這一現象,React 通過 updateDepth 對 Virtual DOM 樹進行層級控制,只會對相同顏色方框內的 DOM 節點進行比較,即同一個父節點下的所有子節點。當發現節點已經不存在,則該節點及其子節點會被完全刪除掉,不會用於進一步的比較。這樣只需要對樹進行一次遍歷,便能完成整個 DOM 樹的比較。

如果出現dom節點跨層級的移動操作,因爲該節點已經不在原來的dom樹層, 所以會直接刪除該節點,在移動後的dom層重建該節點, 可見這種操作的性能代價非常大,所以不推薦這樣做。 可以通過css樣式控制節點的隱藏和顯示來代替節點跨層級移動的操作。
在這裏插入圖片描述

3 component diff

React 是基於組件構建應用的,對於組件間的比較所採取的策略也是簡潔高效。

如果是同一類型的組件,按照原策略繼續比較 virtual DOM tree。

如果不是,則將該組件判斷爲 dirty component,從而替換整個組件下的所有子節點。

對於同一類型的組件,有可能其 Virtual DOM 沒有任何變化,如果能夠確切的知道這點那可以節省大量的 diff 運算時間,因此 React 允許用戶通過 shouldComponentUpdate() 來判斷該組件是否需要進行 diff。

如下圖,當 component D 改變爲 component G 時,即使這兩個 component 結構相似,一旦 React 判斷 D 和 G 是不同類型的組件,就不會比較二者的結構,而是直接刪除 component D,重新創建 component G 以及其子節點。雖然當兩個 component 是不同類型但結構相似時,React diff 會影響性能,但正如 React 官方博客所言:不同類型的 component 是很少存在相似 DOM tree 的機會,因此這種極端因素很難在實現開發過程中造成重大影響的。
在這裏插入圖片描述

4 element diff

當節點處於同一層級時,React diff 提供了三種節點操作,分別爲:INSERT_MARKUP(插入)、MOVE_EXISTING(移動)和 REMOVE_NODE(刪除)。

INSERT_MARKUP,新的 component 類型不在老集合裏, 即是全新的節點,需要對新節點執行插入操作。
MOVE_EXISTING,在老集合有新 component 類型,且 element 是可更新的類型,generateComponentChildren 已調用 receiveComponent,這種情況下 prevChild=nextChild,就需要做移動操作,可以複用以前的 DOM 節點。
REMOVE_NODE,老 component 類型,在新集合裏也有,但對應的 element 不同則不能直接複用和更新,需要執行刪除操作,或者老 component 不在新集合裏的,也需要執行刪除操作。
開發者對同一層級的子節點,可以添加唯一索引進行區分,這樣在diff時,涉及到只是位置變化的,可以只移動元素,避免刪除創建等重複的操作。

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