react源码分析(8)-commit介绍

目录

commit阶段的改动

commit的三个阶段

谈谈 BeforeMutation阶段

谈谈LayOut阶段

总结


2021SC@SDUSC

commit阶段的改动

在这里想分析一下commit阶段,这里指出一个小问题,事实上react最新版本中已经对effectList做了移除,并不寻找更新节点,而是遍历整棵Fiber树,用subtreeTag变量进行优化,详情参照

https://github.com/facebook/react/pull/20595

相当一部分资料介绍了effectList链(这就是一个需要更新的Fiber链表),但现在react的底层架构已经无法看到该变量了。顺便一提,我在这里试图参考其他人的分析,结果发现大部分都在谈effectList,假如react没有出新版本再把effectList加回来的话,那就说明他们解析的版本比较旧了。

这里要结合一下以前说过的调度原理,此时的commit任务是同步执行的,也就是说,此时的更新应当一次完成。

commit的三个阶段

在commit阶段,此时的Fiber节点已经做好标记,可以进行相应的处理以映射在DOM树上,将优先级设为syncLane,优先级最高,通过commitRoot找到commitRootImpl,在此时异步调用所有的effect,然后进入commitBeforeMutationEffects调用commitBeforeMutationEffects_begin,再调用commitBeforeMutationEffects_complete又到commitBeforeMutationEffectsOnFiber方法,终于得到stateNode的getSnapshotBeforeUpdate(这也是react中的生命周期函数,在commit之前得到以前的函数与信息),从而得到类组件中DOM信息的改动并且存储该信息。

接下来通过commitMutationEffects方法对DOM节点进行改动。包括节点的插入,删除以及更新。

现在调用commitLayoutEffects方法,和BeforeMutation对称,调用commitLayoutEffects_begin再调用commitLayoutMountEffects_complete又到commitLayoutEffectOnFiber方法。

谈谈 BeforeMutation阶段

正如上文中所提到的,before mutation阶段中会调度类组件中的 getSnapshotBeforeUpdate以及异步调度函数组件中的useEffect。在这里说一下调用该生命周期函数的位置,位于commitBeforeMutationEffects_complete的commitBeforeMutationEffectsOnFiber中,在这里将Fiber节点的alternate属性赋值给current,代表以前的节点,然后对其进行分类分析,假如current为类组件的话,就会将memoizedProps(赋值给prevProps),memoizedState(赋值给prevState)以及Fiber节点中的stateNode取出(注意,在commit阶段中,stateNode已经更新完成,所以可以作为实例),接下来在该实例中调用 getSnapshotBeforeUpdate方法,并将 prevProps 与 prevState 作为以前的状态作为参数传入。

function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
  const current = finishedWork.alternate;
  const flags = finishedWork.flags;

  if (enableCreateEventHandleAPI) {
    if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
      // Check to see if the focused element was inside of a hidden (Suspense) subtree.
      // TODO: Move this out of the hot path using a dedicated effect tag.
      if (
        finishedWork.tag === SuspenseComponent &&
        isSuspenseBoundaryBeingHidden(current, finishedWork) &&
        doesFiberContain(finishedWork, focusedInstanceHandle)
      ) {
        shouldFireAfterActiveInstanceBlur = true;
        beforeActiveInstanceBlur(finishedWork);
      }
    }
  }

  if ((flags & Snapshot) !== NoFlags) {
    setCurrentDebugFiberInDEV(finishedWork);

    switch (finishedWork.tag) {
      case FunctionComponent:
      case ForwardRef:
      case SimpleMemoComponent: {
        break;
      }
      case ClassComponent: {
        if (current !== null) {
          const prevProps = current.memoizedProps;
          const prevState = current.memoizedState;
          const instance = finishedWork.stateNode;
          // We could update instance props and state here,
          // but instead we rely on them being set during last render.
          // TODO: revisit this when we implement resuming.
          if (__DEV__) {
            if (
              finishedWork.type === finishedWork.elementType &&
              !didWarnAboutReassigningProps
            ) {
              if (instance.props !== finishedWork.memoizedProps) {
                console.error(
                  'Expected %s props to match memoized props before ' +
                    'getSnapshotBeforeUpdate. ' +
                    'This might either be because of a bug in React, or because ' +
                    'a component reassigns its own `this.props`. ' +
                    'Please file an issue.',
                  getComponentNameFromFiber(finishedWork) || 'instance',
                );
              }
              if (instance.state !== finishedWork.memoizedState) {
                console.error(
                  'Expected %s state to match memoized state before ' +
                    'getSnapshotBeforeUpdate. ' +
                    'This might either be because of a bug in React, or because ' +
                    'a component reassigns its own `this.state`. ' +
                    'Please file an issue.',
                  getComponentNameFromFiber(finishedWork) || 'instance',
                );
              }
            }
          }
          const snapshot = instance.getSnapshotBeforeUpdate(
            finishedWork.elementType === finishedWork.type
              ? prevProps
              : resolveDefaultProps(finishedWork.type, prevProps),
            prevState,
          );
          if (__DEV__) {
            const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<mixed>);
            if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
              didWarnSet.add(finishedWork.type);
              console.error(
                '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +
                  'must be returned. You have returned undefined.',
                getComponentNameFromFiber(finishedWork),
              );
            }
          }
          instance.__reactInternalSnapshotBeforeUpdate = snapshot;
        }
        break;
      }
      case HostRoot: {
        if (supportsMutation) {
          const root = finishedWork.stateNode;
          clearContainer(root.containerInfo);
        }
        break;
      }
      case HostComponent:
      case HostText:
      case HostPortal:
      case IncompleteClassComponent:
        // Nothing to do for these component types
        break;
      default: {
        invariant(
          false,
          'This unit of work tag should not have side-effects. This error is ' +
            'likely caused by a bug in React. Please file an issue.',
        );
      }
    }

    resetCurrentDebugFiberInDEV();
  }
}

谈谈LayOut阶段

关于LayOut阶段在Fiber节点的所做的处理,这里简要说明一下在commitLayoutMountEffects_complete中所调用的commitLayoutEffectOnFiber方法。在这其中函数组件调用useLayoutEffect并得到结果,而useEffect则再次进行调度(beforeMutation也进行了异步调度),在这里就体现出了useEffect与useLayoutEffect的不同,重点在运行时机上。而类组件调用了commitUpdateQuque来处理更新队列,还调用了生命函数componentDidMount或componentDidUpdate,这取决与fiber节点中的alternate属性,假如为null,代表挂载,假如有值,代表更新,另外在 componentDidUpdate中会传入三个参数,分别是prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate(作为getSnapshotBeforeUpdate的结果传入)。至此commit阶段完成。在这里记录了一下react渲染过程中的种种具体流程与工作逻辑。能够对react有更清晰的理解与认识。

总结

介绍了commit阶段的新版本源码的改动及其相应的三个阶段(beforeMutation,Mutation,layout)。同时简明扼要的介绍了 beforeMutation与layout阶段的部分具体代码。

关于mutation阶段的具体流程,我将会在下一篇博客中介绍。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值