简单分析React中的EffectList
React中,会遍历EffectList来执行节点操作、生命周期方法、Effect方法,可以把EffectList比作圣诞树上挂的彩灯,而这颗圣诞树就是Fiber树。
为什么会存在EffectList呢?打个比方来说,一颗Fiber树中有一些Fiber节点需要执行ponentDidMount
方法,如果在Fiber树构建完成后,再遍历一次Fiber树,找到需要执行ponentDidMount
方法的Fiber节点,这是非常低效的。
而EffectList就解决了这个问题,在Fiber树构建过程中,每当一个Fiber节点的flags
字段不为NoFlags
时(代表需要执行副作用),就把该Fiber节点添加到EffectList,在Fiber树构建完成后,由Fiber节点串成的彩灯也构建完成了,这样仅仅需要遍历彩灯就行了。
EffectList的收集
EffectList是一个单向链表,firstEffect
代表链表中的第一个Fiber节点,lastEffect
代表链表中的一个Fiber节点。
Fiber树的构建是深度优先的,也就是先向下构建子级Fiber节点,子级节点构建完成后,再向上构建父级Fiber节点,所以EffectList中总是子级Fiber节点在前面。
Fiber节点构建完成的操作执行在pleteUnitOfWork
方法,在这个方法里,不仅会对节点完成构建,也会将有flags
的Fiber节点添加到EffectList。
简化代码如下。
function pleteUnitOfWork(unitOfWork: Fiber): void { let pletedWork = unitOfWork; do { const current = pletedWork.alternate; const returnFiber = pletedWork.return; let next= pleteWork(current, pletedWork, subtreeRenderLanes); // effect list构建 if ( returnFiber !== null && (returnFiber.flags & Inplete) === NoFlags ) { // 层层拷贝 if (returnFiber.firstEffect === null) { returnFiber.firstEffect = pletedWork.firstEffect; } if (pletedWork.lastEffect !== null) { // 说明当前节点是兄弟节点,子节点有effect,已经给returnFiber.lastEffect赋值过了 if (returnFiber.lastEffect !== null) { // 连接兄弟节点的effect returnFiber.lastEffect.nextEffect = pletedWork.firstEffect; } returnFiber.lastEffect = pletedWork.lastEffect; } const flags = pletedWork.flags; // 该fiber节点有effect if (flags > PerformedWork) { // 当前节点有effect连接上effect list if (returnFiber.lastEffect !== null) { returnFiber.lastEffect.nextEffect = pletedWork; } else { // returnFiber没有firstEffect的情况是第一次遇见有effect的节点 returnFiber.firstEffect = pletedWork; } returnFiber.lastEffect = pletedWork; } } // 兄弟元素遍历再到返返回父级 const siblingFiber = pletedWork.sibling; if (siblingFiber !== null) { workInProgress = siblingFiber; return; } pletedWork = returnFiber; workInProgress = pletedWork; } while (pletedWork !== null); }
EffectList实际是像冒泡一样,一层一层不断向上层收集,从第一个有flags
的节点开始记录,每层的新节点都会将上一个节点的firstEffect
和lastEffect
拷贝到自身身上,再供上层节点拷贝。
如以下结构,假如每一个div
都有flags
。
<div id="1"> <div id="4"/> <div id="2"> <div id="3"/> </div> </div>
最终形成的EffectList为
firstEffect => div4 lastEffect => div1
因为Fiber树的构建深度优先,所有div4
先完成pleteWork
,构建firstEffect
。
EffectList遍历是从firstEffect
开始,通过每一个节点的nextEffect
找到下一个节点。
firstEffect => div4 div4.nextEffect => div3 div3.nextEffect => div2 div2.nextEffect => div1
初次Render时的EffectList
在React中,会对初次Mount有一个性能优化,其中的Fiber节点的flags
不会包含placement
,对应的DOM节点不会遍历加入DOM树,而是在创建DOM节点时就已经加入DOM树了,只有rootFiber节点FiberRootNode
的flags
会包含placement
。
EffectList是不会包含root
节点的,所以需要将root
节点也添加到EffectList,这样才会正确的执行placement
,让DOM树在页面呈现 。
let firstEffect; // 把根节点finishedWork也连接进去 if (finishedWork.flags > PerformedWork) { if (finishedWork.lastEffect !== null) { finishedWork.lastEffect.nextEffect = finishedWork; firstEffect = finishedWork.firstEffect; } else { firstEffect = finishedWork; } } else { // 根节点没有effect. firstEffect = finishedWork.firstEffect; }
EffectList的遍历
EffectList的主要是用于Layout阶段生命周期方法的执行和DOM的操作。
// 处理getSnapshotBeforeUpdate,调度useEffect nextEffect = firstEffect; do { mitBeforeMutationEffects(); } while (nextEffect !== null); // DOM操作 nextEffect = firstEffect; do { mitMutationEffects(root, renderPriorityLevel); } while (nextEffect !== null); // 生命周期方法的执行 nextEffect = firstEffect; do { mitLayoutEffects(root, lanes); } while (nextEffect !== null);
在这Layout阶段的这3个方法里,会遍历nextEffect
,每执行完一个,就重新指向firstEffect
。Layout阶段具体操作就不细讲了。
EffectList不是全局变量,只是在Fiber树创建过程中,一层层向上收集有effect
的Fiber节点,最终的root
节点就会收集到所有有effect
到Fiber节点,我们就把这条包含effect
节点的链表叫做EffectList。
由于收集的过程是深度优先,子级会先被收集,所以遍历的时候也会先操作子级,所以如果有面试官问子级和父级的生命周期或者useEffect
谁先执行,就很清楚的知道会先执行子级操作了。
以上就是简单分析React中的EffectList的详细内容,更多关于React中的EffectList的资料请关注狼蚁SEO其它相关文章!
编程语言
- 甘肃哪有关键词排名优化购买方式有哪些
- 甘肃SEO如何做网站优化
- 河南seo关键词优化怎么做电话营销
- 北京SEO优化如何做QQ群营销
- 来宾百度关键词排名:提升您网站曝光率的关键
- 卢龙关键词优化:提升您网站排名的策略与技巧
- 山东网站优化的注意事项有哪些
- 四川整站优化怎样提升在搜索引擎中的排名
- 疏附整站优化:提升网站性能与用户体验的全新
- 海南seo主要做什么工作售后服务要做到哪些
- 荣昌百度网站优化:提升您网站的搜索引擎排名
- 河北seo网站排名关键词优化如何做SEO
- 江西优化关键词排名推广售后保障一般有哪些
- 古浪SEO优化:提升你的网站可见性
- 西藏网站排名优化怎么把网站排名在百度首页
- 如何提升阳东百度快照排名:详尽指南