壹、什麽是diff算法?
為了增強用戶體驗,React從版本16開始將 同步更新 重構成了 可中斷的異步更新 ,即采用了新的Reconciler(協調器,用於找出變化的組件),而新的Reconciler中采用了fiber架構。fiber架構的原理在此不再詳細解釋,我們目前只需要知道fiber節點可以保存dom信息,fiber節點構成的樹叫fiber樹,而更新dom是要用到‘雙緩存技術’,即比較舊的fiber樹與此次要渲染的jsx對象,返回新的fiber樹進行渲染。 在舊fiber樹與jsx對象比較時,決定哪些節點要復用的過程,就是diff算法 。
由於diff本身也會帶來性能消耗,為了降低算法復雜度,React對diff做了 三個預設限制 :
更新後
如果沒有key會走第二條限制,有了key,react就可以判斷div和p節點是存在的,可以復用,只需要交換順序。
diff算法會根據不同的jsx對象執行不同的處理函數,根據jsx對象的不同,我們可以分為兩類 :
1.JSX對象(之後都用newChildren表示)的類型為object、number、string,代表同級只有壹個節點
2. newChildren的類型為Array,代表同級有多個節點。
二、單節點diff
對於單節點diff,用壹個流程圖就可以解釋
更新後
由於 key的默認值為null ,所以更新前與更新後滿足key相同且元素類型不同,那麽我們要刪除更新前的三個div節點,新增p節點
三、多節點diff
對於多節點diff, 我們要 遍歷newChildren和oldFiber 進行比較。由於React團隊發現dom節點壹般有更新,增加,刪除這三種操作,而更新更為頻繁,所以他們設置更新的優先級高於增加刪除。基於以上原因,在多節點diff算法的實現中有兩層遍歷, 第壹層遍歷處理更新的節點,第二層遍歷處理更新以外的節點 。
第壹層遍歷
遍歷newChildren與oldFiber, 判斷節點是否可復用,如果可以復用,則繼續遍歷。
如果不能復用,分為兩種情況:
第二層遍歷
第二層遍歷從第壹層遍歷的結束位開始
第壹層遍歷結束後有4種結果
首先我們要判斷newChildren中遍歷到的節點,在oldFiber中是否存在,基於此,React將oldFiber中的節點以key-oldfiber 鍵值對的形式存在Map中,只需要newChildren的key,就可以判斷oldFiber中有沒有相應的節點。
如果oldFiber中沒有相應的節點,則將newChildren生成的fiber打上placement標記
如果有相應的節點,將它的索引記為oldIndex,與上壹次可復用節點在oldFiber的索引位置lastPlacedIndex比較,如果每次可復用的節點在上壹次可復用右邊就說明位置沒有變化 ,即
若 oldIndex >=lastPlacedIndex, 說明相對位置沒有變化 ,那麽令lastPlacedIndex=oldIndex
若 oldIndex<lastPlacedIndex, 代表本節點需要向右移動 。
例如:
參考文檔 :
React技術揭秘 (iamkasong.com)