一提到React,學過的人都會想到提升性能的兩大神奇特點:虛擬DOM & diff算法。React diff做爲Virtual DOM的加速器,其算法的改進優化是React整的界面渲染的基礎,以及性能提升的保障。雖然開發中不須要知道其運行機制,可是理解以後有助於更好的理解React組件的生命週期,以及優化React程序。前端
React diff表示什麼?表示React針對傳統的diff算法進行了React風格的優化!react
計算一棵樹形結構轉換成另外一棵樹形結構的最少操做,是一個複雜且值得研究的問題。算法
傳統 diff 算法經過循環遞歸對節點進行依次對比,效率低下,算法複雜度達到 O(n^3),其中 n 是樹中節點的總數。O(n^3) 到底有多可怕,這意味着若是要展現1000個節點,就要依次執行上十億次的比較。代價過高。性能
若是 React 只是單純的引入 diff 算法而沒有任何的優化改進,那麼其效率是遠遠沒法知足前端渲染所要求的性能。優化
所以,想要將 diff 思想引入 Virtual DOM,就須要設計一種穩定高效的 diff 算法,而 React 作到了!設計
那麼,React diff 究竟是如何實現的呢?3d
傳統的diff算法的複雜度爲O(n^3),顯然沒法知足性能要求。Facebook工程師經過大膽的策略,將O(n^3)複雜度簡化成了O(n),怎麼作到的呢?component
基於以上三個前提策略,React團隊對傳統diff算法優化基於三個策略(《深刻React技術棧》等講的確實有點難理解且模糊,這邊通過理解給出了本身的理解)cdn
基於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的方式展現上述過程
React是基於組件構建應用的,對於組件間的比較所採用的策略也是簡潔高效。
以下圖,當 component D 改變爲 component G 時,即便這兩個 component 結構類似,一旦 React 判斷 D 和 G 是不一樣類型的組件,就不會比較兩者的結構,而是直接刪除 component D,從新建立 component G 以及其子節點。雖然當兩個 component 是不一樣類型但結構類似時,React diff 會影響性能,但正如 React 官方博客所言:不一樣類型的 component 是不多存在類似 DOM tree 的機會,所以這種極端因素很難在實現開發過程當中形成重大影響的。
而若是上圖中左一中的D節點只是單純的改變什麼state,update就行了。
全部同一層級的子節點.他們均可以經過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,即哪裏須要改變,就改變哪裏!