React Diff理解

前言

一提到React,學過的人都會想到提升性能的兩大神奇特點:虛擬DOM & diff算法。React diff做爲Virtual DOM的加速器,其算法的改進優化是React整的界面渲染的基礎,以及性能提升的保障。雖然開發中不須要知道其運行機制,可是理解以後有助於更好的理解React組件的生命週期,以及優化React程序。前端

React diff表示什麼?表示React針對傳統的diff算法進行了React風格的優化!react

傳統diff算法

計算一棵樹形結構轉換成另外一棵樹形結構的最少操做,是一個複雜且值得研究的問題。算法

傳統 diff 算法經過循環遞歸對節點進行依次對比,效率低下,算法複雜度達到 O(n^3),其中 n 是樹中節點的總數。O(n^3) 到底有多可怕,這意味着若是要展現1000個節點,就要依次執行上十億次的比較。代價過高。性能

若是 React 只是單純的引入 diff 算法而沒有任何的優化改進,那麼其效率是遠遠沒法知足前端渲染所要求的性能。優化

所以,想要將 diff 思想引入 Virtual DOM,就須要設計一種穩定高效的 diff 算法,而 React 作到了!設計

那麼,React diff 究竟是如何實現的呢?3d

React diff優化

傳統的diff算法的複雜度爲O(n^3),顯然沒法知足性能要求。Facebook工程師經過大膽的策略,將O(n^3)複雜度簡化成了O(n),怎麼作到的呢?component

diff策略

  • Web UI 中 DOM 節點跨層級的移動操做特別少,能夠忽略不計。
  • 擁有相同類的兩個組件將會生成類似的樹形結構,擁有不一樣類的兩個組件將會生成不一樣的樹形結構。
  • 對於同一層級的一組子節點,它們能夠經過惟一 id 進行區分。

基於以上三個前提策略,React團隊對傳統diff算法優化基於三個策略(《深刻React技術棧》等講的確實有點難理解且模糊,這邊通過理解給出了本身的理解)cdn

  • a->tree diff
  • b->component diff
  • c->element diffd

優化策略a: tree diff

基於tree diff策略,React對Virtual DOM樹進行 分層比較、層級控制,只對相同顏色框內的節點進行比較(同一父節點的所有子節點),當發現某一子節點不在了直接刪除該節點以及其全部子節點,不會用於進一步的比較,在算法層面上就是說只須要遍歷一次就能夠了,而無需在進行沒必要要的比較,便能完成整個DOM樹的比較。blog

如圖:

同屬於分層比較、層級控制範疇,還會出現DOM節點跨層級的移動操做(React中這種狀況DOM節點不穩定,損害性能,因此開發中不推薦這種狀況的出現),React diff怎麼解決的呢?以下圖狀況:

上面描述的是同一層次不一樣DOM節點範疇,React diff用趨近於‘暴力’的方式,並非把A B C 直接拼接到 D 節點上,而是刪除A B C 三個節點以後在 D 下面在建立的 A B C。這裏不作詳細分析,想直觀理解該過程,建議閱讀這篇用在生命週期裏打log的方式展現上述過程

優化策略b: component diff

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

  • 對於同一類型的組件,根據Virtual DOM是否變化也分兩種,能夠用shouldComponentUpdate()判斷Virtual DOM是否發生了變化,若沒有變化就不須要在進行diff,這樣能夠節省大量時間,若變化了,就對相關節點進行update
  • 對於非同一類的組件,則將該組件判斷爲 dirty component,從而替換整個組件下的全部子節點。

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

而若是上圖中左一中的D節點只是單純的改變什麼state,update就行了。

優化策略c: element diff

全部同一層級的子節點.他們均可以經過key來區分-----並遵循策略a、b。

沒通過優化的算法,實現新老交替的方法是將A B C D所有刪除以後,在新建B A D C,這樣的實現方法顯然很垃圾,React diff怎麼優化呢?是經過爲每個節點添加key值標識。

新老集合所包含的節點,如上圖所示,新老集合進行 diff 差別化對比,經過 key 發現新老集合中的節點都是相同的節點,所以無需進行節點刪除和建立,只須要將老集合中節點的位置進行移動,更新爲新集合中節點的位置,此時 React 給出的 diff 結果爲:B、D 不作任何操做,A、C 進行移動操做,便可。

上述分析的是新老集合中存在相同節點可是位置不一樣,要是有新加入的節點且有舊節點須要刪除呢?這裏再也不囉嗦,以下圖:

加了key的好處:

若是不加key,map遍歷的時候控制檯發出warn,既然是warn就說明不加也能實現遍歷,可是是通過刪除、建立、插入實現,這樣的話損害性能可想而知,而加上key就能夠有助於React diff算法結合Virtual DOM找到最合適的方式進行diff,最大限度的實現高效diff,即哪裏須要改變,就改變哪裏!

總結

  • React 經過分層求異的策略,對 tree diff 進行算法優化;
  • React 經過相同類生成類似樹形結構,不一樣類生成不一樣樹形結構的策略,對 component diff 進行算法優化;
  • React 經過設置惟一 key的策略,對 element diff 進行算法優化;
  • React 經過制定大膽的 diff 策略,將 O(n3) 複雜度的問題轉換成 O(n) 複雜度的問題;
  • 建議,開發時保持穩定的DOM結構有助於性能的提高;

參考

相關文章
相關標籤/搜索