React 的調(diào)和算法Diffing 算法策略詳解
算法策略
React的調(diào)和算法,主要發(fā)生在render階段,調(diào)和算法并不是一個特定的算法函數(shù),而是指在調(diào)和過程中,為提高構建workInProcess樹的性能,以及Dom樹更新的性能,而采用的一種策略,又稱diffing算法。 在React 的官網(wǎng)上描述“Diffing” 算法時,提到了“diffing two trees”,但是在源碼實現(xiàn)時,并不是創(chuàng)建好兩棵樹后,再從上往下的diffing這兩棵樹。這個diffing發(fā)生在搭建子節(jié)點時, 實際是新生成的ReactElement 與 current樹上fibe節(jié)點的diffing。 為了將diffing算法的時間復雜度控制在O(n)(樹diff的時間復雜度涉及到樹的編輯距離,可以看這里), 采用了如下策略:
只比較同層級的節(jié)點,(貌似這一點沒有在官網(wǎng)中提到)
對于單節(jié)點比較,如果當前節(jié)點type 和 key 不相同,不再比較其下子節(jié)點,直接刪掉該節(jié)點及其下整棵子樹,根據(jù)ReactElement重新生成節(jié)點樹。因為React認為不同類型的組件生成的樹形結構不一樣,不必復用。
如果子節(jié)點是數(shù)組,可根據(jù)唯一的key值定位節(jié)點進行比較,這樣即使子節(jié)點順序發(fā)生變化,也可以根據(jù)key值進行復用。
值得注意的是,所有節(jié)點的diffing都會比較key,key 默認值為null。若是沒有設置,則null是恒等于null的,認為key是相同的。
單節(jié)點diffing
單個節(jié)點進行diffing時,會diffing兩組屬性:fibe.elementType vs ReactElement.type , fibe.key vs ReactElement.key, 如果這兩組屬性都相等,數(shù)據(jù)結構的物理空間會有如下復用邏輯(詳見源碼reconcileSingleElement函數(shù)):
- 如果current fibe 存在 alternate 節(jié)點,(這意味著這個fibe節(jié)點之前參與過調(diào)和),則復用該alternate節(jié)點的物理空間;否則需要clone current fibe節(jié)點,占用新的物理空間
- 對應的instance 或者 Dom 會被復用
- current fibe 下的child樹也會被直接掛載過來(下一步遞歸子節(jié)點時,會被使用)
如果兩組屬性有一個不相等:
- fibe 根據(jù)ReactElement重新創(chuàng)建
- 對應的instance和Dom 也會重建
- child 樹全部刪除。
如果生成的ReactElement 是單節(jié)點,但是對應的current樹上是多節(jié)點時,會從逐一查找有沒有匹配的,找到匹配的,其他的都刪除;找不到,全部刪除。例如Couter組件有如下邏輯:當點擊次數(shù)大于10時,隱藏點擊按鈕。當?shù)竭_第10次時,span節(jié)點會被復用。
class Counter extends React.Component{
state={
count:0
}
addCount = ()=>{
const count = this.state.count+1;
this.setState({count})
}
componentDidUpdate(){
console.log("updated")
}
render(){
return this.state.count < 10 ? [
<button onClick={this.addCount}>點擊</button>
<span>點擊次數(shù):{this.state.count}</span>
]:<span>點擊次數(shù):{this.state.count}</span>;
}
}
數(shù)組節(jié)點diffing
數(shù)組節(jié)點進行diffing時,流程比較復雜:(源碼見reconcileChildrenArray函數(shù))

中間有兩次重要的遍歷,第一次按index遍歷,新舊節(jié)點依次比較,遇到key值不匹配的立即中斷遍歷。 第二次遍歷,對剩下的舊節(jié)點建立 “key - 節(jié)點”的Map表,遍歷剩下的新節(jié)點,按key值從表中查找舊節(jié)點進行比較。以下是三種case下,新舊節(jié)點匹配比較的情況。

key值的使用要求
從單節(jié)點和數(shù)組節(jié)點的diffing上看,key值主要是為了減少新建。為了保證diffing時新建舊節(jié)點能匹配上,key值使用時有如下注意:
- 得穩(wěn)定,如果key值每次都變化(比如使用了隨機數(shù)),diffing時,新舊節(jié)全部匹配不上,將會引起大量的新建;
- 必須得唯一,如果key值不唯一,在建立“key - 節(jié)點”的Map表時,會遺漏和錯亂,導致頁面更新錯誤。
對于單節(jié)點,diffing時key值也會用到,不要認為其沒用隨便亂設置。 也有一些不規(guī)范的用法,對單節(jié)點使用key值來實現(xiàn)組件的銷毀和重建,但這種用法是不符合React的設計理念的。
例如,有一個日志組件,內(nèi)部拉取日志數(shù)據(jù),對外部沒有數(shù)據(jù)依賴。但是在需求迭代時,在同頁面增加了審批操作,審批后,后端會新增一條日志數(shù)據(jù),這時候會希望前端頁面實時的展示新增的日志數(shù)據(jù),會需要觸發(fā)日志組件重新拉取數(shù)據(jù)。如果不使用向上提升數(shù)據(jù)的方式來解決問題,可以給該組件指定一個隨機的key,當審批操作完成時,修改key值,則組件就會重新構建。但這種方式的開銷較高,整個組件樹都會銷毀重建??梢圆捎闷渌鉀Q方案代替,例如可以給該組件指定ref,當審批完成后,通過ref調(diào)用該組件的刷新函數(shù),重新獲取數(shù)據(jù),更新組件。
對于數(shù)組節(jié)點,key值主要是在子節(jié)點位置發(fā)生大的錯位時,會起到關鍵作用。 對于普通的末尾新增,和末尾刪除,第一次index遍歷就可以匹配完,不會進入第二次key map的遍歷。 因為key值對“穩(wěn)定”和“唯一”性這兩個要求,一般前端會需要后端對list數(shù)據(jù)提供一個唯一標識,對于聚合接口,會給后端增加額外工作,這種情況,可以先了解數(shù)據(jù)的變化特性,再決定是否真的需要設置key。
對于上面的日志組件,日志是一個列表,新增的日志,都是插在第一行,如果不設置key,基本上所有的子節(jié)點都要更新。如果設置key,就需要后端服務為每一條日志增加“永遠”惟一的標識符,例如id。曾經(jīng)經(jīng)歷過一次因為key值的唯一性變化,導致數(shù)據(jù)更新時頁面展示錯誤的案例。
到此這篇關于React 的調(diào)和算法(Diffing 算法)的文章就介紹到這了,更多相關React Diffing 算法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
React實現(xiàn)文件上傳和斷點續(xù)傳功能的示例代碼
這篇文章主要為大家詳細介紹了React實現(xiàn)文件上傳和斷點續(xù)傳功能的相關知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-02-02
react-pdf實現(xiàn)將pdf文件轉(zhuǎn)為圖片,用于頁面展示
這篇文章主要介紹了react-pdf實現(xiàn)將pdf文件轉(zhuǎn)為圖片,用于頁面展示問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
react中的forwardRef 和memo的區(qū)別解析
forwardRef和memo是React中用于性能優(yōu)化和組件復用的兩個高階函數(shù),本文給大家介紹react中的forwardRef 和memo的區(qū)別及適用場景,感興趣的朋友跟隨小編一起看看吧2023-10-10
詳解react-router 4.0 下服務器如何配合BrowserRouter
這篇文章主要介紹了詳解react-router 4.0 下服務器如何配合BrowserRouter,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12
使用react-activation實現(xiàn)keepAlive支持返回傳參
本文主要介紹了使用react-activation實現(xiàn)keepAlive支持返回傳參,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-05-05
React-View-UI組件庫封裝Loading加載中源碼
這篇文章主要介紹了React-View-UI組件庫封裝Loading加載樣式,主要包括組件介紹,組件源碼及組件測試源碼,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06
react的嚴格模式和解決react useEffect執(zhí)行兩次問題
文章總結:本文詳細探討了React中useEffect執(zhí)行兩次的問題,主要歸因于React的嚴格模式,嚴格模式在開發(fā)模式下會故意重復調(diào)用一些生命周期方法,以幫助開發(fā)者發(fā)現(xiàn)潛在的問題,包括不安全的生命周期、過時的ref API、廢棄的findDOMNode方法、意外的副作用等2025-01-01

