React18.2源码解析之render阶段(下)

148 阅读2分钟

记录学习过程,如有错误欢迎指出

前两篇文章我们把performConcurrentWorkOnRoot讲了,本篇文章会将和performConcurrentWorkOnRoot对应的另外一个函数performSyncWorkOnRoot

开始之前(必看)

React version 18.2.0

DEV代码可以忽略,下面正文我使用省略号就代表是dev相关的代码,包含hydrate字样的也请忽略,那是服务端渲染相关,望周知

我使用深度优先(🐶)的方式讲解:即遇到函数先进入函数,执行完函数后,又退回之前的函数.而不是在一个函数中讲完了再讲函数中执行的函数(希望能听懂我在说什么^v^)

因为使用了深度优先的方式讲解,耦合比较重,不建议跳着看

performSyncWorkOnRoot(未完)

function performSyncWorkOnRoot(root) {
  ....

  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
    throw new Error('Should not already be working.');
  }

  //消除被动效果,在performConcurrentWorkOnRoot中有讲到过,这里不再赘述
  flushPassiveEffects();

  //获取下一个车道
  let lanes = getNextLanes(root, NoLanes);
  if (!includesSomeLane(lanes, SyncLane)) {
  
    //安排进行调度
    ensureRootIsScheduled(root, now());
    return null;
  }
  
  //renderRootSync函数也在上一章中讲解了
  let exitStatus = renderRootSync(root, lanes);
  if (root.tag !== LegacyRoot && exitStatus === RootErrored) {
    // 如果第一次renderRootSync出错了,那么react将再尝试一次,最后如果还是失败了
    // 那么将会抛弃这个节点
    const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
    // 判断第二次是否失败
    if (errorRetryLanes !== NoLanes) {
      lanes = errorRetryLanes;
      // 如果失败,调用recoverFromConcurrentError
      // 我们进入recoverFromConcurrentError看一下
      exitStatus = recoverFromConcurrentError(root, errorRetryLanes);
    }
  }
    
  .....
  

recoverFromConcurrentError

function recoverFromConcurrentError(root, errorRetryLanes) {

  // 保存上一次错误的原因
  const errorsFromFirstAttempt = workInProgressRootConcurrentErrors;

  if (isRootDehydrated(root)) {
    // prepareFreshStack:准备双缓存
    const rootWorkInProgress = prepareFreshStack(root, errorRetryLanes);
    rootWorkInProgress.flags |= ForceClientRender;
    ....
  }
  
    // 同步调和
  const exitStatus = renderRootSync(root, errorRetryLanes);
  // 判断调和状态
  if (exitStatus !== RootErrored) {
    // Successfully finished rendering on retry

    //第一次出现的错误被恢复了,现在将这次操作记录下来
    const errorsFromSecondAttempt = workInProgressRootRecoverableErrors;
    workInProgressRootRecoverableErrors = errorsFromFirstAttempt;
    // 保证两次调用的`因果关系`
    if (errorsFromSecondAttempt !== null) {
      queueRecoverableErrors(errorsFromSecondAttempt);
    }
  } else {
    // The UI failed to recover.
  }
  return exitStatus;
}

performSyncWorkOnRoot(完)


  ....
  
// 出现致命错误的情况
  if (exitStatus === RootFatalErrored) {
    const fatalError = workInProgressRootFatalError;
    prepareFreshStack(root, NoLanes);
    markRootSuspended(root, lanes);
    ensureRootIsScheduled(root, now());
    throw fatalError;
  }

  // 没有调和完成
  if (exitStatus === RootDidNotComplete) {
    throw new Error('Root did not complete. This is a bug in React.');
  }

  // 现在就得到了两颗一直的树
  // 因为是同步渲染,不管有什么事情暂停,这里都会提交
  const finishedWork: Fiber = (root.current.alternate: any);
  root.finishedWork = finishedWork;
  root.finishedLanes = lanes;
  // Sync模式下的进入的commit
  commitRoot(
    root, /**fiberRootNode */
    workInProgressRootRecoverableErrors, /**null */
    workInProgressTransitions, /**null */
  );

  // 退出之前的回调
  ensureRootIsScheduled(root, now());

  return null;
}

总结

至此,render阶段就结束了,总结一下,render阶段就是对sync和concurrent模式的判断,从而执行不同的逻辑,多看几遍还是可以理解的,后面就是commit的阶段了,commit阶段也是react最后阶段,执行完毕后浏览器中就会呈现页面

我也画完了React三个阶段流程图,后面我会上传到github,欢迎大家star

记录学习过程,如有错误欢迎指出