一文掌握React?組件樹遍歷技巧
引言
本文對(duì)應(yīng)的 react
版本是 18.2.0
下面的 dom
結(jié)構(gòu)react
內(nèi)部是如何遍歷的
const App = () => { return ( <div> <button>+1</button> <A count={0} /> </div> ); }; const A = (props) => { useEffect(() => { console.log(props.count); }, [props.count]); return <div>{props.count}</div>; };
react 內(nèi)部遍歷核心邏輯:
- 在
render
時(shí)調(diào)用commitPassiveUnmountOnFiber
函數(shù) commitPassiveUnmountOnFiber
處理不同的WorkTag
,并調(diào)用recursivelyTraversePassiveUnmountEffects
recursivelyTraversePassiveUnmountEffects
根據(jù)當(dāng)前Fiber
的子節(jié)點(diǎn)有沒(méi)有passive effect
(useEffect
,useLayoutEffect
)來(lái)決定是否遍歷當(dāng)前Fiber
的子節(jié)點(diǎn)- 如果子節(jié)點(diǎn)有
passive effect
,則優(yōu)先遍歷子節(jié)點(diǎn) (深度優(yōu)先),直到找到最終的葉子節(jié)點(diǎn),退出當(dāng)前循環(huán) - 然后進(jìn)入兄弟節(jié)點(diǎn),開始遍歷兄弟節(jié)點(diǎn)的子節(jié)點(diǎn)
- 具體從哪個(gè)兄弟節(jié)點(diǎn)開始遍歷,
react
選擇的是離退出循環(huán)的那個(gè)葉子節(jié)點(diǎn)的父節(jié)點(diǎn),檢查有沒(méi)有子節(jié)點(diǎn),以此循環(huán)遍歷
- 具體從哪個(gè)兄弟節(jié)點(diǎn)開始遍歷,
- 直到最后找到所有有
passive effect
的節(jié)點(diǎn)
- 如果子節(jié)點(diǎn)有
commitPassiveUnmountOnFiber(root.current); function commitPassiveUnmountOnFiber(finishedWork) { // 省略了處理不同的 WorkTag recursivelyTraversePassiveUnmountEffects(finishedWork); } function recursivelyTraversePassiveUnmountEffects(parentFiber) { // 省略了其他處理 if (parentFiber.subtreeFlags & PassiveMask) { let child = parentFiber.child; while (child !== null) { commitPassiveUnmountOnFiber(child); child = child.sibling; } } }
所以對(duì)于這段 dom 的遍歷邏輯是:
- 首先從根組件開始
FiberRootNode
,取到current
- 也就是說(shuō)
FiberRootNode.current
是div#root
這是一個(gè)fiber
,它的tag
是3
- 也就是說(shuō)
- 由于
App
的子組件有passive effect
,所以會(huì)進(jìn)入App
組件,它的tag
是0
App
組件中節(jié)點(diǎn)是<div>
,<di >
的tag
是5
<div>
下面有兩個(gè)子元素<button>
、<A>
- 先遍歷
<button>
它的tag
是5
<button>
內(nèi)部只有一個(gè)文本節(jié)點(diǎn),沒(méi)有passive effect
- 所以
react
不遍歷了(跳出當(dāng)前遍歷的循環(huán),也就是button
這條不在遍歷了)
- 所以
- 跳出循環(huán)后,查看
button
的兄弟節(jié)點(diǎn),它的兄弟節(jié)點(diǎn)是<A>
,<A>
的tag
是0
- 由于
<A>
節(jié)點(diǎn)的子節(jié)點(diǎn)沒(méi)有passive effect
,所以跳出循環(huán),結(jié)束整個(gè)遍歷
總結(jié)
- 從跟節(jié)點(diǎn)開始遍歷
- 當(dāng)前組件的子組件有沒(méi)有
passive effect
- 采取深度優(yōu)先
- 如果
dom
節(jié)點(diǎn)內(nèi)有函數(shù)組件,則這個(gè)dom
會(huì)被遍歷,否則不會(huì)遍歷 - 如果當(dāng)前
fiber
下的所有子fiber
都沒(méi)有passive effect
,則這一整個(gè)都鏈表都不會(huì)被遍歷 - 如果當(dāng)前
fiber
只有dom
,則這些dom
也不會(huì)遍歷
總的來(lái)說(shuō)組件會(huì)不會(huì)別遍歷看 fiber
有沒(méi)有 passive effect
:
- 有,一定會(huì)被遍歷
- 沒(méi)有,下面兩種情況會(huì)被遍歷,其他情況不會(huì)被遍歷
- 是
passive effect
的父組件 - 和
passive effect
組件是兄弟組件
- 是
passive effect
指的是 useEffect
,useLayoutEffect
遍歷邏輯如下圖所示
圖中畫綠色勾的都會(huì)被遍歷,紅色勾是遍歷的順序
以上就是一文掌握React 組件樹遍歷技巧的詳細(xì)內(nèi)容,更多關(guān)于React 組件樹遍歷的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
關(guān)于React動(dòng)態(tài)修改元素樣式的三種方式
這篇文章主要介紹了關(guān)于React動(dòng)態(tài)修改元素樣式的三種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08詳解一個(gè)基于react+webpack的多頁(yè)面應(yīng)用配置
這篇文章主要介紹了詳解一個(gè)基于react+webpack的多頁(yè)面應(yīng)用配置,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-01-01關(guān)于React16.0的componentDidCatch方法解讀
這篇文章主要介紹了關(guān)于React16.0的componentDidCatch方法解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05Input標(biāo)簽自動(dòng)校驗(yàn)功能去除實(shí)現(xiàn)
這篇文章主要為大家介紹了Input標(biāo)簽的自動(dòng)拼寫檢查功能去除實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07react路由v6版本NavLink的兩個(gè)小坑及解決
這篇文章主要介紹了react路由v6版本NavLink的兩個(gè)小坑及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10react路由守衛(wèi)的實(shí)現(xiàn)(路由攔截)
react不同于vue,通過(guò)在路由里設(shè)置meta元字符實(shí)現(xiàn)路由攔截。本文就詳細(xì)的介紹一下,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08React渲染機(jī)制及相關(guān)優(yōu)化方案
這篇文章主要介紹了react中的渲染機(jī)制以及相關(guān)的優(yōu)化方案,內(nèi)容包括react渲染步驟、concurrent機(jī)制以及產(chǎn)生作用的機(jī)會(huì),簡(jiǎn)單模擬實(shí)現(xiàn) concurrent mode,基于作業(yè)調(diào)度優(yōu)先級(jí)的思路進(jìn)行項(xiàng)目?jī)?yōu)化的兩個(gè)hooks,感興趣的小伙伴跟著小編一起來(lái)看看吧2023-07-07