一文掌握React?組件樹遍歷技巧
引言
本文對應(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時調(diào)用commitPassiveUnmountOnFiber函數(shù) commitPassiveUnmountOnFiber處理不同的WorkTag,并調(diào)用recursivelyTraversePassiveUnmountEffectsrecursivelyTraversePassiveUnmountEffects根據(jù)當(dāng)前Fiber的子節(jié)點(diǎn)有沒有passive effect(useEffect,useLayoutEffect)來決定是否遍歷當(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)
- 具體從哪個兄弟節(jié)點(diǎn)開始遍歷,
react選擇的是離退出循環(huán)的那個葉子節(jié)點(diǎn)的父節(jié)點(diǎn),檢查有沒有子節(jié)點(diǎn),以此循環(huán)遍歷
- 具體從哪個兄弟節(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;
}
}
}
所以對于這段 dom 的遍歷邏輯是:
- 首先從根組件開始
FiberRootNode,取到current- 也就是說
FiberRootNode.current是div#root這是一個fiber,它的tag是3
- 也就是說
- 由于
App的子組件有passive effect,所以會進(jìn)入App組件,它的tag是0 App組件中節(jié)點(diǎn)是<div>,<di >的tag是5<div>下面有兩個子元素<button>、<A>
- 先遍歷
<button>它的tag是5 <button>內(nèi)部只有一個文本節(jié)點(diǎn),沒有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)沒有passive effect,所以跳出循環(huán),結(jié)束整個遍歷
總結(jié)
- 從跟節(jié)點(diǎn)開始遍歷
- 當(dāng)前組件的子組件有沒有
passive effect - 采取深度優(yōu)先
- 如果
dom節(jié)點(diǎn)內(nèi)有函數(shù)組件,則這個dom會被遍歷,否則不會遍歷 - 如果當(dāng)前
fiber下的所有子fiber都沒有passive effect,則這一整個都鏈表都不會被遍歷 - 如果當(dāng)前
fiber只有dom,則這些dom也不會遍歷
總的來說組件會不會別遍歷看 fiber 有沒有 passive effect:
- 有,一定會被遍歷
- 沒有,下面兩種情況會被遍歷,其他情況不會被遍歷
- 是
passive effect的父組件 - 和
passive effect組件是兄弟組件
- 是
passive effect 指的是 useEffect,useLayoutEffect
遍歷邏輯如下圖所示
圖中畫綠色勾的都會被遍歷,紅色勾是遍歷的順序

以上就是一文掌握React 組件樹遍歷技巧的詳細(xì)內(nèi)容,更多關(guān)于React 組件樹遍歷的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
關(guān)于React動態(tài)修改元素樣式的三種方式
這篇文章主要介紹了關(guān)于React動態(tài)修改元素樣式的三種方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08
詳解一個基于react+webpack的多頁面應(yīng)用配置
這篇文章主要介紹了詳解一個基于react+webpack的多頁面應(yīng)用配置,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01
關(guān)于React16.0的componentDidCatch方法解讀
這篇文章主要介紹了關(guān)于React16.0的componentDidCatch方法解讀,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05
Input標(biāo)簽自動校驗(yàn)功能去除實(shí)現(xiàn)
這篇文章主要為大家介紹了Input標(biāo)簽的自動拼寫檢查功能去除實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
react路由守衛(wèi)的實(shí)現(xiàn)(路由攔截)
react不同于vue,通過在路由里設(shè)置meta元字符實(shí)現(xiàn)路由攔截。本文就詳細(xì)的介紹一下,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
React渲染機(jī)制及相關(guān)優(yōu)化方案
這篇文章主要介紹了react中的渲染機(jī)制以及相關(guān)的優(yōu)化方案,內(nèi)容包括react渲染步驟、concurrent機(jī)制以及產(chǎn)生作用的機(jī)會,簡單模擬實(shí)現(xiàn) concurrent mode,基于作業(yè)調(diào)度優(yōu)先級的思路進(jìn)行項(xiàng)目優(yōu)化的兩個hooks,感興趣的小伙伴跟著小編一起來看看吧2023-07-07

