1. 程式人生 > >深入理解React虛擬DOM

深入理解React虛擬DOM

統計 ldr 即使 src 控制 執行 對比 問題 如果

一、什麽是虛擬DOM

虛擬DOM可以看做一棵模擬了DOM樹的JavaScript對象樹。比如:

技術分享圖片
 1 var element = {
 2     element: ‘ul‘,
 3     props: {
 4         id:"ulist"
 5     },
 6     children: [
 7     { element: ‘li‘, props: { id:"first" }, children: [‘這是第一個List元素‘] },
 8     { element: ‘li‘, props: { id:"second" }, children: [‘這是第二個List元素‘] }
 9     ]
10 }
技術分享圖片

二、為什麽使用虛擬DOM

在傳統的 Web 應用中,我們往往會把數據的變化實時地更新到用戶界面中,於是每次數據的微小變動都會引起 DOM 樹的重新渲染。

虛擬DOM的目的是將所有操作累加起來,統計計算出所有的變化後,統一更新一次DOM。

技術分享圖片

三、虛擬DOM的原理

當Node節點的更新,虛擬DOM會比較兩棵DOM樹的區別,保證最小化的DOM操作,使得執行效率得到保證。

計算兩棵樹的常規算法是O(n^3)級別,所以需要優化深度遍歷的算法。React diff算法的時間復雜度為O(n)。

React diff 算法

技術分享圖片

React 分別對 tree diff、component diff

以及 element diff 進行算法優化。

1、tree diff

技術分享圖片

DOM 節點跨層級的移動操作少到可以忽略不計,針對這一現象,

React 通過 updateDepth 對 Virtual DOM 樹進行層級控制,

只會對相同顏色方框內的 DOM 節點進行比較,即同一個父節點下的所有子節點。

當發現節點已經不存在,則該節點及其子節點會被完全刪除掉,不會用於進一步的比較。

這樣只需要對樹進行一次遍歷,便能完成整個 DOM 樹的比較。

2、component diff

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

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

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

技術分享圖片

當 component D 改變為 component G 時,即使這兩個 component 結構相似,

一旦 React 判斷 D 和 G 是不同類型的組件,就不會比較二者的結構,

而是直接刪除 component D,重新創建 component G 以及其子節點。

3、element diff

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

技術分享圖片

新老集合所包含的節點,如下圖所示,新老集合進行 diff 差異化對比,

通過 key 發現新老集合中的節點都是相同的節點,因此無需進行節點刪除和創建,

只需要將老集合中節點的位置進行移動,更新為新集合中節點的位置,

此時 React 給出的 diff 結果為:B、D 不做任何操作,A、C 進行移動操作,即可。

總結

  • React 通過制定大膽的 diff 策略,將 O(n3) 復雜度的問題轉換成 O(n) 復雜度的問題;

  • React 通過分層求異的策略,對 tree diff 進行算法優化;

  • React 通過相同類生成相似樹形結構,不同類生成不同樹形結構的策略,對 component diff 進行算法優化;

  • React 通過設置唯一 key的策略,對 element diff 進行算法優化;

深入理解React虛擬DOM