简朴阐明React中的EffectList

React中,会遍历EffectList来执行节点操纵、生命周期要领、Effect要领,可以把EffectList比作圣诞树上挂的彩灯,而这颗圣诞树就是Fiber树。

为什么会存在EffectList呢?打个例如来说,一颗Fiber树中有一些Fiber节点需要执行componentDidMount要领,假如在Fiber树构建完成后,再遍历一次Fiber树,找到需要执行componentDidMount要领的Fiber节点,这长短常低效的。

而EffectList就办理了这个问题,在Fiber树构建进程中,每当一个Fiber节点的flags字段不为NoFlags时(代表需要执行副浸染),就把该Fiber节点添加到EffectList,在Fiber树构建完成后,由Fiber节点串成的彩灯也构建完成了,这样仅仅需要遍历彩灯就行了。

EffectList的收集

EffectList是一个单向链表,firstEffect代表链表中的第一个Fiber节点,lastEffect代表链表中的最后一个Fiber节点。

Fiber树的构建是深度优先的,也就是先向下构建子级Fiber节点,子级节点构建完成后,再向上构建父级Fiber节点,所以EffectList中老是子级Fiber节点在前面。

Fiber节点构建完成的操纵执行在completeUnitOfWork要领,在这个要领里,不只会对节点完成构建,也会将有flags的Fiber节点添加到EffectList。

简化代码如下。

function completeUnitOfWork(unitOfWork: Fiber): void { let completedWork = unitOfWork; do { const current = completedWork.alternate; const returnFiber = completedWork.return; let next= completeWork(current, completedWork, subtreeRenderLanes); // effect list构建 if ( returnFiber !== null && (returnFiber.flags & Incomplete) === NoFlags ) { // 层层拷贝 if (returnFiber.firstEffect === null) { returnFiber.firstEffect = completedWork.firstEffect; } if (completedWork.lastEffect !== null) { // 说明当前节点是兄弟节点,子节点有effect,已经给returnFiber.lastEffect赋值过了 if (returnFiber.lastEffect !== null) { // 毗连兄弟节点的effect returnFiber.lastEffect.nextEffect = completedWork.firstEffect; } returnFiber.lastEffect = completedWork.lastEffect; } const flags = completedWork.flags; // 该fiber节点有effect if (flags > PerformedWork) { // 当前节点有effect毗连上effect list if (returnFiber.lastEffect !== null) { returnFiber.lastEffect.nextEffect = completedWork; } else { // returnFiber没有firstEffect的环境是第一次碰见有effect的节点 returnFiber.firstEffect = completedWork; } returnFiber.lastEffect = completedWork; } } // 兄弟元素遍历再到返返回父级 const siblingFiber = completedWork.sibling; if (siblingFiber !== null) { workInProgress = siblingFiber; return; } completedWork = returnFiber; workInProgress = completedWork; } while (completedWork !== null); }

EffectList实际是像冒泡一样,一层一层不绝向上层收集,从第一个有flags的节点开始记录,每层的新节点城市将上一个节点的firstEffect和lastEffect拷贝到自身身上,再供上层节点再次拷贝。

如以下布局,如果每一个div都有flags。

<div> <div/> <div> <div/> </div> </div>

最终形成的EffectList为

firstEffect => div4 lastEffect => div1

因为Fiber树的构建深度优先,所有div4先完成completeWork,构建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 { commitBeforeMutationEffects(); } while (nextEffect !== null); // DOM操纵 nextEffect = firstEffect; do { commitMutationEffects(root, renderPriorityLevel); } while (nextEffect !== null); // 生命周期要领的执行 nextEffect = firstEffect; do { commitLayoutEffects(root, lanes); } while (nextEffect !== null);

在这Layout阶段的这3个要领里,会遍历nextEffect,每执行完一个,就从头指向firstEffect。Layout阶段详细操纵就不细讲了。

总结

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsjydz.html