react源码学习2-初次渲染流程

86 阅读2分钟

上次我们分析到了updateContainer方法,本节继续跟踪 在上节给出的updateContainer简化版的代码中,有如下几个重要的步骤:

const update = createUpdate(eventTime, lane)
update.payload = {element};
enqueueUpdate(current, update);
scheduleUpdateOnFiber(current, lane, eventTime);

createUpdate的逻辑很简单,就是创建了一个描述更新的对象

export function createUpdate(eventTime: number, lane: Lane): Update<*> {
  const update: Update<*> = {
    eventTime,
    lane,

    tag: UpdateState,
    payload: null,
    callback: null,

    next: null,
  };
  return update;
}

在enqueueUpdate方法中,就是将刚刚创建的update放入到fiber的updateQueue中

export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
  const updateQueue = fiber.updateQueue;
  if (updateQueue === null) {
    // Only occurs if the fiber has been unmounted.
    return;
  }

  const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
  const pending = sharedQueue.pending;
  if (pending === null) {
    // This is the first update. Create a circular list.
    update.next = update;
  } else {
    update.next = pending.next;
    pending.next = update;
  }
  sharedQueue.pending = update;

这里的sharedQueue.pending,构成了一个单向的环形链表

接下来的scheduleUpdateOnFiber方法,里面判断很多,但无论是哪种判断,基本可归结为两条路径:

performSyncWorkOnRoot
ensureRootIsScheduled -> performSyncWorkOnRoot / performConcurrentWorkOnRoot

在performSyncWorkOnRoot方法里,又有两个主要步骤:

exitStatus = renderRootSync(root, lanes);
commitRoot(root);

renderRootSync简化后代码如下:

function renderRootSync(root: FiberRoot, lanes: Lanes) {
  const prevExecutionContext = executionContext;
  executionContext |= RenderContext;
  const prevDispatcher = pushDispatcher();

  // If the root or lanes have changed, throw out the existing stack
  // and prepare a fresh one. Otherwise we'll continue where we left off.
  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
    prepareFreshStack(root, lanes);
    startWorkOnPendingInteractions(root, lanes);
  }
  do {
    try {
      workLoopSync();
      break;
    } catch (thrownValue) {
      handleError(root, thrownValue);
    }
  } while (true);
  workInProgressRoot = null;
  workInProgressRootRenderLanes = NoLanes;

  return workInProgressRootExitStatus;
}

renderRootSync里面,主要做的事情就是通过调用

workLoopSync();

开启了事件循环 workLoopSync主要做的工作就是创建fiber并收集effect

function workLoopSync() {
  // Already timed out, so perform work without checking if we need to yield.
  while (workInProgress !== null) {
    performUnitOfWork(workInProgress);
  }
}

renderRootSync里还有一个工作很关键: 在最开始,通过调用prepareFreshStack给workingInProgress赋值,将workingInProgress作为构建新的fiber树的起点

prepareFreshStack中给workInProgress赋值的语句是:
workInProgress = createWorkInProgress(root.current, null);

createWorkInProgress其实就是复用现有的rootFiber,创建一个新的rootFiber

renderRootSync执行完回到performSyncWorkOnRoot之后,就会将Fiber构建好(beginWork),同时也在fiberRoot上收集好了effectList(completeWork)

然后在接下来的commitRoot方法中,遍历执行所有的effect,创建真实DOM

在commitRoot中,实际上还做了一些执行优先级相关的工作,内部又调用了commitRootImpl方法:

function commitRoot(root) {
  const renderPriorityLevel = getCurrentPriorityLevel();
  runWithPriority(
    ImmediateSchedulerPriority,
    commitRootImpl.bind(null, root, renderPriorityLevel),
  );
  return null;
}

在commitRootImpl中,开启了三轮循环,分别从firstEffect开始,循环到lastEffect为止 三轮循环分别执行了

commitBeforeMutationEffects()
commitMutationEffects(root, renderPriorityLevel)
commitLayoutEffects(root, lanes)

实际在这个过程之前,还做了一些事情,例如useEffect的回调等处理

在before阶段,即commitBeforeMutationEffects,做的事情有:

  • 调用getSnapshotBeforeUpdate生命周期钩子
  • 调度useEffect

commitLayoutEffects一共做了两件事:

  • commitLayoutEffectOnFiber(调用生命周期钩子例如componentDidMount componentDidUpdate,setState里的回调,以及hook相关操作)
  • commitAttachRef(赋值 ref)