diff算法一直是React系統最核心的部分,而且因爲演化自傳統diff,使得比較方式從O(n^3)降級到O(n),而後又改爲了鏈表方式,可謂是變化萬千。node
傳統diff算法須要循環比較兩棵樹,全部節點的循環,那麼單純比較次數就是O(n^2),n*n算法
P L A A / \ / \ / \ / \ B D ====> D B / \ C C
刷刷刷,每次都須要循環遍歷,因而有如下的查找過程:segmentfault
PA->LA PA->LB PA->LC PA->LD PB->LA ...
除了查找過程消耗了O(n^2)以外,找到差別後還要計算最小轉換方式,最終結果爲O(n^3)。瀏覽器
因此,傳統的diff算法的時間複雜度爲O(n^3)。異步
若是React運用這種算法,那麼節點過多,將會有大量的開銷,雖然CPU的秒速達到30億次計算,但依舊是很是耗費性能的。oop
有沒有什麼方式能夠下降時間複雜度呢?性能
因而,React15對傳統的diff作了一些限制,使得時間複雜度變爲了O(n)。.net
《深刻React技術棧》這本書,給出了三種Diff策略分析,文字描述太過抽象,直接表述以下:線程
Tree diff、Component diff、Element diff3d
什麼是Tree diff?先上圖:
首先,進行同級比較,並不是循環比較。這樣比較次數就降爲一層一次,時間複雜度直接降爲O(n)
若是同級相同位置節點不同,則直接刪除替換,簡單粗暴。
而對於節點移動,一樣道理,也是簡單粗暴的刪除重建。以下圖所示(圖中第四步應該是刪除左側的整棵A樹):
很少說,先上圖:
其實component diff至關因而子樹的diff,基本方案和tree diff是一致的,若是如上圖D變爲G,那麼直接刪除D這一整棵樹,而後從新渲染G樹。
依舊是簡單粗暴。
對於同一節點的元素,diff算法提供了三種操做:插入、移動、刪除。仍是先上圖:
此時的操做,是B、D不作任何操做,AC移動到相應位置【前提是都有相同的key】
若是,此時的key不相同,全都發生了變化,那麼節點全都是要刪除從新構建,將會消耗大量性能。
React16相比React15的Diff算法發生了很大的變化,其中最主要就是引入了Fiber循環任務調度算法。
Fiber是什麼?幹了什麼?
Fiber在diff階段,作了以下的操做:
一、能夠隨時將diff操做進行任務拆分。
二、diff階段的每一個任務能夠隨時執行或者停止。
三、diff階段任務調度優先級控制。
因此,Fiber至關因而,在15的diff算法階段,作了優先級的任務調度控制,
因此,Fiber是根據一個fiber節點(VDOM節點)來拆分,以fiber node爲一個任務單元,一 個組件實例都是一個任務單元。任務循環中,每處理完一個fiber node,能夠中斷/掛起/恢復。
它又是如何可以進行這樣的異步操做的呢?這就不得不說一個方法:requestIdleCallback
瀏覽器提供的requestIdleCallback API中的Cooperative Scheduling可讓瀏覽器在空閒時間執行回調(開發者傳入的方法),在回調參數中能夠獲取到當前幀(16ms)剩餘的時間。利用這個信息能夠合理的安排當前幀須要作的事情,若是時間足夠,那繼續作下一個任務,若是時間不夠就歇一歇,調用requestIdleCallback來獲知主線程不忙的時候,再繼續作任務
Fiber Node是什麼?
鏈表!
將要處理的節點,存在鏈表結構,那麼就可以作到節點複用。【這大概是Fiber的核心吧】
大致上的Diff引入了Fiber以後,咱們就增長了更多的鏈表複用功能,經過這一點,咱們可使得React Diff的性能獲得提高。
其實,這篇文章着重講的仍是React15的diff,React 16的diff並未詳細探討,接下來會出一篇文章,單獨講解React 16的Diff策略。不過React 16Diff策略的核心Fiber是不可錯過的點。
《深刻React技術棧》
https://segmentfault.com/a/1190000016723305
https://www.jianshu.com/p/3ba0822018cf
https://www.jianshu.com/p/21a445066d51?from=timeline
https://www.zhihu.com/question/66851503/answer/246766239
https://blog.csdn.net/P6P7qsW6ua47A2Sb/article/details/82322033
https://blog.csdn.net/VhWfR2u02Q/article/details/100011830