本文对应的 react
版本是 18.2.0
下面的 dom
结构react
内部是如何遍历的
const App = () => {
return (
);
};
const A = (props) => {
useEffect(() => {
console.log(props.count);
}, [props.count]);
return {props.count};
};
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
组件中节点是,
的tag
是5
-
下面有两个子元素
、
- 先遍历
它的
tag
是5
内部只有一个文本节点,没有
passive effect
- 所以
react
不遍历了(跳出当前遍历的循环,也就是button
这条不在遍历了)
- 跳出循环后,查看
button
的兄弟节点,它的兄弟节点是,
的
tag
是0
- 由于
节点的子节点没有
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
源码文章javascriptreact.js前端源码源码分析阅读 42本作品系原创,采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议
前端学习笔记
一步一个脚印 - 先遍历
uccs
一只刚刚接触前端的小鸟
594 声望74 粉丝推荐阅读手把手教你写一份优质的前端技术简历
不知不觉一年一度的秋招又来了,你收获了哪些大厂的面试邀约,又拿了多少offer呢?你身边是不是有挺多人技术比你差,但是却拿到了很多大厂的offer呢?其实,要想面试拿offer,首先要过得了简历那一关。如果一份简...tonychen赞 155阅读 18.1k评论 5
正则表达式实例
收集在业务中经常使用的正则表达式实例,方便以后进行查找,减少工作量。常用正则表达式实例1. 校验基本日期格式 {代码...} {代码...} 2. 校验密码强度密码的强度必须是包含大小写字母和数字的组合,不能使用特殊...寒青赞 57阅读 8.8k评论 12
JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...jenemy赞 50阅读 7.9k评论 12
再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...libinfs赞 42阅读 7.1k评论 12
「多图预警」完美实现一个@功能
一天产品大大向 boss 汇报完研发成果和产品业绩产出,若有所思的走出来,劲直向我走过来,嘴角微微上扬。产品大大:boss 对我们的研发成果挺满意的,balabala...(内心 OS:不听,讲重点)产品大大:咱们的客服 I...wuwhs赞 33阅读 3.8k评论 5
一文搞懂秒杀系统,欢迎参与开源,提交PR,提高竞争力。早日上岸,升职加薪。
前言秒杀和高并发是面试的高频考点,也是我们做电商项目必知必会的场景。欢迎大家参与我们的开源项目,提交PR,提高竞争力。早日上岸,升职加薪。知识点详解秒杀系统架构图秒杀流程图秒杀系统设计这篇文章一万多...王中阳Go赞 37阅读 2.9k评论 1
CSS transition 小技巧!如何保留 hover 的状态?
欢迎关注我的公众号:前端侦探通常情况下,hover 是无法保存状态的。鼠标移入触发额外样式,一旦移出就还原了 {代码...} 这就意味着,如果需要保留hover的状态,可能就不得不借助JS了,比如下面是某某书院的首页...XboxYan赞 30阅读 4.1k评论 2
-
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。