The corresponding react
version is18.2.0
Before officially starting, we need to understand the properties fiber
of :deletions
This attribute stores the deleted ones in the current node fiber
, and this array is assigned in commit
the stage
If there is a deleted node, the property value is an array, if there is no deleted node, the property value isnull
const A = () => {
useEffect(() => {
return () => {
console.log("A unmount");
};
}, []);
return <div>文本A</div>;
};
const B = () => {
useEffect(() => {
return () => {
console.log("B unmount");
};
}, []);
return <div>文本B</div>;
};
复制代码
If App
the component is written like this, then deletions
the value of is[FiberNode, FiberNode]
const App(){
const [count, setCount] = useState(0)
return <div>
{count % 2 === 0 && <A />}
{count % 2 === 0 && <B />}
<div onClick={()=> setCount(count+1)}>+1</div>
</div>
}
复制代码
If App
the component is written like this, then deletions
the value of is[FiberNode]
const App(){
const [count, setCount] = useState(0)
return <div>
{count % 2 === 0 && <><A /><B /></>}
<div onClick={()=> setCount(count+1)}>+1</div>
</div>
}
复制代码
For the second case, the componentreact
and the component are considered as a whole, so the value of isA
B
deletions
[FiberNode]
Handle deletions of the current node
react
fiber tree
When traversing , the current fiber
one will be processed first deletions
, and then the next one will be traversed after processingfiber
Now we already know that what is saved deletions
in is fiber
the child node that is deleted under the current
At this time, react
it will traverse the array, and then execute the function returned bydeletions
eachfiber
passive effect
But there is a problem, if deletions
in fiber
has child nodes, then these child nodes will also be deleted, how react
to ?
There are two situations to discuss here:
- Deleted
fiber
has no children:<div>{xxxx && <A />}</div>
- The deleted
fiber
has child nodes:<div>{xxxx && <><A /><B /></>}</div>
-->
A deleted fiber has no children:<div>{xxxx && <A />}</div>
This situation is better understood
当遍历到 div
时,因为 <A/>
节点会被卸载,所以在 div
的 deletions
保存了一个 <A/>
的 fiber
遍历 deletions
数组,执行 <A/>
的 passive effect
返回的函数
如下图所示:
删除的 fiber 有子节点:<div>{xxxx && <><A /><B /></>}</div>
这种情况就比较复杂了
当遍历到 div
时,<></>
节点会被卸载,所以在 div
的 deletions
保存了一个 <></>
的 fiber
遍历 deletions
数组,执行 fiber
的 passive effect
返回的函数,对于 <></>
来说是不存在的 passive effect
那么这个时候就要去遍历它的 child.fiber
,也就是 <A/>
和 <B/>
首先拿到第一个 fiber,也就是 <A/>
,然后执行 <A/>
的 passive effect
返回的函数,这步比较好理解
child = fiber.child;
if (child !== null) {
nextEffect = child;
}
复制代码
这里遍历也是深度优先,遍历一个 child
,执行一个 passive effect
返回函数,然后再遍历下一个 child
(这边 <A />
已经是叶子节点了)
然后拿到第二个 fiber
,也就是 <B/>
,然后执行 <B/>
的 passive effect
返回的函数,这步就不太好理解了
child = fiber.child;
if (child !== null) {
nextEffect = child;
} else {
commitPassiveUnmountEffectsInsideOfDeletedTree_complete(deletedSubtreeRoot);
}
复制代码
这里要注意的是:
react
在寻找有 passive effect
的 fiber
时,只遍历到有 passive effect
的 fiber
, 像 div
这种没有 passive effect
就不会遍历
但是在处理 deletions
,react
会遍历所有的 fiber
,也就是说从当前的 fiber
开始,一直往下遍历到叶子节点,这个叶子节点是指文本节点这种,往下不会有节点了(对于 A
组件来说 文本A
是文本节点)
然后在开始往上遍历,往上遍历是调用 commitPassiveUnmountEffectsInsideOfDeletedTree_complete
函数,直到遍历到 deletionRoot
,在向上遍历的过程中会检查是否有 sibling
,如果有说明 sibling
还没被处理,这样就找到了 <B/>
,然后执行 <B/>
的 passive effect
返回的函数
如下图所示:
向下遍历和向上遍历
在处理 deletions
时,对于每个 deletedNode
,都先向下遍历,然后再向上遍历
- 向下遍历:
commitPassiveUnmountEffectsInsideOfDeletedTree_begin
(深度优先,优先处理左边的节点) - 向上遍历:
commitPassiveUnmountEffectsInsideOfDeletedTree_complete
(之后再处理右边节点)
总结
1. 遍历 deletions 数组:
react
在处理deletions
时,先沿着fiber tree
向下遍历,如果有passive effect
返回的函数,则执行- 一直遍历到没有
child
的fiber
,再向上遍历,处理sibling
- 再向上遍历时,如果如果遇到
sibling
,再向下遍历,向下遍历时遇到passive effect
返回的函数,则执行 - 如此循环直到遍历到
deletedNode
,结束遍历
2. 结合掌握 React 组件树遍历技巧
- 遍历寻找有
passive effect
节点react
从根组件向下遍历,如果没有passive effect
,则不会遍历
- 遍历时,如果遇到当前节点有
deletions
时,会暂停寻找passive effect
节点- 进入遍历
deletions
数组
- 进入遍历
react 遍历 deletions 完整逻辑如下图所示:
图中绿色部分是遍历 deletionsNode
过程,红色部分是遍历寻找 passive effect
过程
往期文章
- 深入探究 React 原生事件的工作原理
- React Lane 算法:一文详解 8 种 Lane 操作
- 剖析 React 任务调度机制:scheduleCallback 实现原理
- 掌握 React 组件树遍历技巧
更多 react
源码文章