lz-lee / React-Source-Code

React源码阅读笔记
11 stars 0 forks source link

renderRoot #6

Open lz-lee opened 5 years ago

lz-lee commented 5 years ago

renderRoot

function renderRoot(
  root: FiberRoot,
  isYieldy: boolean,
  isExpired: boolean,
): void {
  isWorking = true;
  ReactCurrentOwner.currentDispatcher = Dispatcher;

  const expirationTime = root.nextExpirationTimeToWorkOn;

  // Check if we're starting from a fresh stack, or if we're resuming from
  // previously yielded work.
  // 说明将要执行的任务 root 和 expirationTime 和 nextRenderExpirationTime、nextRoot 预期的不一样, 可能是之前任务被高优先级的任务打断了。
  if (
    expirationTime !== nextRenderExpirationTime ||
    root !== nextRoot ||
    nextUnitOfWork === null //  更新结束 fiber 的 child,下一个节点, 首次为 null
  ) {
    // Reset the stack and start working from the root.
    // 重置
    resetStack();
    nextRoot = root;
    // root.nextExpirationTimeToWorkOn;
    nextRenderExpirationTime = expirationTime;
    // 将 fiber 拷贝成 workInProgress 对象操作,不直接操作 fiber 节点
    nextUnitOfWork = createWorkInProgress(
      nextRoot.current,
      null,
      nextRenderExpirationTime,
    );
    root.pendingCommitExpirationTime = NoWork;

  let didFatal = false;

  startWorkLoopTimer(nextUnitOfWork);
  // 开始循环 workLoop
  do {
    try {
      // 同步或过期任务则 isYieldy 为 false 表示不可中断
      workLoop(isYieldy);
    } catch (thrownValue) {
      // ... catch 错误
      // 致命错误
      if (didFatal) { }

      // workLoop break 中断的错误
      if (nextUnitOfWork !== null) { }

      // 可处理的错误
      if (nextRenderDidError) { }
    }
    break; // 遇到了某种错误跳出
  } while (true);

  // ...
  const rootWorkInProgress = root.current.alternate;
  // Ready to commit.
  // 最后 commit root
  onComplete(root, rootWorkInProgress, expirationTime);
}

workLoop

function workLoop(isYieldy) {
  // 同步或过期任务 isYieldy 为false 不可中断
  if (!isYieldy) {
    // Flush work without yielding
    while (nextUnitOfWork !== null) {
      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    }
  } else {
    // Flush asynchronous work until the deadline runs out of time.
    // !shouldYield() 还有剩余时间用来更新
    while (nextUnitOfWork !== null && !shouldYield()) {
      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    }
  }
}

performUnitOfWork

beginWork

function beginWork(
  // RootFiber
  current: Fiber | null,
  // 与 nextUnitOfWork 对应,下一个将要更新的节点
  workInProgress: Fiber,
  // root.nextExpirationTimeToWorkOn 表示一次更新内最高优先级的更新
  renderExpirationTime: ExpirationTime,
): Fiber | null {
  // 节点自身的过期时间
  const updateExpirationTime = workInProgress.expirationTime;

  if (current !== null) {
    const oldProps = current.memoizedProps;
    const newProps = workInProgress.pendingProps;
    if (
      // 前后两次props 相同
      oldProps === newProps &&
      // context 相关
      !hasLegacyContextChanged() &&
      // 没有更新
      (updateExpirationTime === NoWork ||
      // 更新优先级没有当前更新优先级高
        updateExpirationTime > renderExpirationTime)
    ) {
      // This fiber does not have any pending work. Bailout without entering
      // the begin phase. There's still some bookkeeping we that needs to be done
      // in this optimized path, mostly pushing stuff onto the stack.
      switch (workInProgress.tag) {
        // ...优化的过程
      }
      // 跳过当前节点及其子节点的更新
      return bailoutOnAlreadyFinishedWork(
        current,
        workInProgress,
        renderExpirationTime,
      );
    }
  }

  // Before entering the begin phase, clear the expiration time.
  workInProgress.expirationTime = NoWork;
  // 节点是有更新的情况
  switch (workInProgress.tag) {
    // ...根据节点类型,执行对应组件类型的更新
  }
}

bailoutOnAlreadyFinishedWork

function bailoutOnAlreadyFinishedWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderExpirationTime: ExpirationTime,
): Fiber | null {
  cancelWorkTimer(workInProgress);

  if (current !== null) {
    // Reuse previous context list
    workInProgress.firstContextDependency = current.firstContextDependency;
  }

  // Check if the children have any pending work.
  // 子节点最高优先级的更新
  const childExpirationTime = workInProgress.childExpirationTime;
  if (
    // 没有更新
    childExpirationTime === NoWork ||
    // 子树的优先级低
    childExpirationTime > renderExpirationTime
  ) {
    // The children don't have any work either. We can skip them.
    // TODO: Once we add back resuming, we should check if the children are
    // a work-in-progress set. If so, we need to transfer their effects.
    // 直接跳过子树更新
    return null;
  } else {
    // This fiber doesn't have work, but its subtree does. Clone the child
    // fibers and continue.
    // 子树有更新的情况
    // 当前节点不需要更新,说明 child 没有更新,把老的 workInProgress.child 复制一份,并返回
    cloneChildFibers(current, workInProgress);
    return workInProgress.child;
  }
}

根据 fiber 的 tag 类型进行更新

updateHostRoot

function scheduleRootUpdate(
  current: Fiber, // RootFiber
  element: ReactNodeList, // ReactDOM.render 的第一个参数
  expirationTime: ExpirationTime,
  callback: ?Function,
) {

  // 创建 update,这个update tag 为 UpdateState
  const update = createUpdate(expirationTime);
  // Caution: React DevTools currently depends on this property
  // being called "element".
  // 这里的 payload 相当于 state ,跟 setState 调用效果相同
  update.payload = {element};

  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    warningWithoutStack(
      typeof callback === 'function',
      'render(...): Expected the last optional `callback` argument to be a ' +
        'function. Instead received: %s.',
      callback,
    );
    update.callback = callback;
  }
  // 为 RootFiber 添加 updateQueue
  enqueueUpdate(current, update);

  scheduleWork(current, expirationTime);
  return expirationTime;
}
function updateHostRoot(current, workInProgress, renderExpirationTime) {
  pushHostRootContext(workInProgress);
  const updateQueue = workInProgress.updateQueue;
  invariant(
    updateQueue !== null,
    'If the root does not have an updateQueue, we should have already ' +
      'bailed out. This error is likely caused by a bug in React. Please ' +
      'file an issue.',
  );
  const nextProps = workInProgress.pendingProps;
  const prevState = workInProgress.memoizedState;
  // 首次 prevState 为null
  const prevChildren = prevState !== null ? prevState.element : null;
  // 执行 processUpdateQueue 将 updateQueue 执行得到有个 element 属性的新的state, 
  processUpdateQueue(
    workInProgress,
    updateQueue,
    nextProps,
    null,
    renderExpirationTime,
  );
  // 拿到 scheduleRootUpdate 时的 update.payload 
  const nextState = workInProgress.memoizedState;
  // Caution: React DevTools currently depends on this property
  // being called "element".
  // 拿到 element 即 <App />
  const nextChildren = nextState.element;
  // 如果相同,则跳过更新,
  // 一般不会在 RootFiber 上创建更新,而是在 ReactDOM.render 传入的第一个参数那个节点(app)上创建更新
  if (nextChildren === prevChildren) {
    // If the state is the same as before, that's a bailout because we had
    // no work that expires at this time.
    // 服务端渲染相关,复用节点
    resetHydrationState();
    return bailoutOnAlreadyFinishedWork(
      current,
      workInProgress,
      renderExpirationTime,
    );
  }
  const root: FiberRoot = workInProgress.stateNode;
  // 服务端渲染相关
  if (
    (current === null || current.child === null) &&
    root.hydrate &&
    enterHydrationState(workInProgress)
  ) {

    workInProgress.effectTag |= Placement;
    // 认为这是第一次渲染
    workInProgress.child = mountChildFibers(
      workInProgress,
      null,
      nextChildren,
      renderExpirationTime,
    );
  } else {
    // Otherwise reset hydration state in case we aborted and resumed another
    // root.
    // ReactDOM.render -> 首次渲染时,current 和 workInProgress 都是存在的, 那么会走 reconcileChildFibers 更新调和
    reconcileChildren(
      current,
      workInProgress,
      nextChildren,
      renderExpirationTime,
    );
    resetHydrationState();
  }
  return workInProgress.child;
}