好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

简单分析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 id = "1" >

  < div id = "4" />

  < div id = "2" >

   < div id = "3" />

  </ 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阶段具体操作就不细讲了。

总结

EffectList不是全局变量,只是在Fiber树创建过程中,一层层向上收集有 effect 的Fiber节点,最终的 root 节点就会收集到所有有 effect 到Fiber节点,我们就把这条包含 effect 节点的链表叫做EffectList。

由于收集的过程是深度优先,子级会先被收集,所以遍历的时候也会先操作子级,所以如果有面试官问子级和父级的生命周期或者 useEffect 谁先执行,就很清楚的知道会先执行子级操作了。

以上就是简单分析React中的EffectList的详细内容,更多关于React中的EffectList的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.cn/post/6947168516394975239

查看更多关于简单分析React中的EffectList的详细内容...

  阅读:40次