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

以上就是一文掌握React 組件樹遍歷技巧的詳細(xì)內(nèi)容,更多關(guān)于React 組件樹遍歷的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
淺談redux以及react-redux簡單實(shí)現(xiàn)
這篇文章主要介紹了淺談redux以及react-redux簡單實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08
React函數(shù)組件傳參的實(shí)現(xiàn)
React函數(shù)組件通過接受props實(shí)現(xiàn)組件間的數(shù)據(jù)傳遞,通過組件標(biāo)簽的屬性向子組件傳遞數(shù)據(jù),并在子組件中通過參數(shù)接收,還可以使用ES6的解構(gòu)賦值,函數(shù)也能作為props傳遞,以實(shí)現(xiàn)父子組件間的交互和通信,下面就來具體了解一下2024-09-09
React事件監(jiān)聽和State狀態(tài)修改方式
這篇文章主要介紹了React事件監(jiān)聽和State狀態(tài)修改方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
React實(shí)現(xiàn)翻頁時(shí)鐘的代碼示例
本文給大家介紹了React實(shí)現(xiàn)翻頁時(shí)鐘的代碼示例,翻頁時(shí)鐘把數(shù)字分為上下兩部分,翻頁效果的實(shí)現(xiàn)需要通過設(shè)置 position 把所有的數(shù)組放在同一個(gè)位置疊加起來,文中有詳細(xì)的代碼講解,需要的朋友可以參考下2023-08-08

