本文对应的 react
版本是 18.2.0
上面的 dom
构造 react
外部是如何遍历的
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
外部遍历外围逻辑:
- 在
render
时调用commitPassiveUnmountOnFiber
函数 commitPassiveUnmountOnFiber
解决不同的WorkTag
,并调用recursivelyTraversePassiveUnmountEffects
-
recursivelyTraversePassiveUnmountEffects
依据以后Fiber
的子节点有没有passive effect
(useEffect
,useLayoutEffect
)来决定是否遍历以后Fiber
的子节点- 如果子节点有
passive effect
,则优先遍历子节点 (深度优先),直到找到最终的叶子节点,退出以后循环 -
而后进入兄弟节点,开始遍历兄弟节点的子节点
- 具体从哪个兄弟节点开始遍历,
react
抉择的是离退出循环的那个叶子节点的父节点,查看有没有子节点,以此循环遍历
- 具体从哪个兄弟节点开始遍历,
- 直到最初找到所有有
passive effect
的节点
- 如果子节点有
代码简化:
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
,所以会进入App
组件,它的tag
是0
-
App
组件中节点是<div>
,<di >
的tag
是5
<div>
上面有两个子元素<button>
、<A>
- 先遍历
<button>
它的tag
是5
-
<button>
外部只有一个文本节点,没有passive effect
- 所以
react
不遍历了(跳出以后遍历的循环,也就是button
这条不在遍历了)
- 所以
- 跳出循环后,查看
button
的兄弟节点,它的兄弟节点是<A>
,<A>
的tag
是0
- 因为
<A>
节点的子节点没有passive effect
,所以跳出循环,完结整个遍历
总结
- 从跟节点开始遍历
- 以后组件的子组件有没有
passive effect
- 采取深度优先
- 如果
dom
节点内有函数组件,则这个dom
会被遍历,否则不会遍历 - 如果以后
fiber
下的所有子fiber
都没有passive effect
,则这一整个都链表都不会被遍历 - 如果以后
fiber
只有dom
,则这些dom
也不会遍历
总的来说组件会不会别遍历看 fiber
有没有 passive effect
:
- 有,肯定会被遍历
-
没有,上面两种状况会被遍历,其余状况不会被遍历
- 是
passive effect
的父组件 - 和
passive effect
组件是兄弟组件
- 是
passive effect
指的是 useEffect
,useLayoutEffect
遍历逻辑如下图所示
图中画绿色勾的都会被遍历,红色勾是遍历的程序
往期文章
- 深刻探索 React 原生事件的工作原理
- React Lane 算法:一文详解 8 种 Lane 操作
- 分析 React 任务调度机制:scheduleCallback 实现原理
更多 react
源码文章