深入理解React虛擬DOM
一、什麽是虛擬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
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