React19源码系列之 root.render过程


在创建react项目的时候,入口文件总是有这样一行代码

root.render(<App />)

所以 root.render() 执行是怎样的? 下面就来看看。

之前的文章就提及,root是一个 ReactDOMRoot 对象,其原型链上有 render 和 unmount 方法。

ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render =
  // $FlowFixMe[missing-this-annot]
  function (children: ReactNodeList): void {
    // this就是ReactDOMRoot实例,
    // this._internalRoot就是fiberRoot对象
    const root = this._internalRoot;
    if (root === null) {
      throw new Error('Cannot update an unmounted root.');
    }
    //执行更新
    updateContainer(children, root, null, null);
  };

root.render流程图

updateContainer

updateContainer 函数是 React 中用于触发更新容器内容的核心函数,它的主要作用是启动一个更新流程,将 新的 React 元素(element)渲染到 指定的容器(container)中。该函数会获取当前的 根 Fiber 节点,请求一个更新车道(lane)来确定更新的优先级,然后调用 updateContainerImpl 函数执行具体的更新操作,最后返回更新车道。

函数参数含义

  • element:类型为 ReactNodeList,表示要渲染到容器中的 React 元素或元素列表。它可以是单个 React 元素,也可以是多个元素组成的数组。
  • container:类型为 OpaqueRoot,是一个不透明的根对象,实际上代表了 FiberRootNode,包含了整个 React 应用的根节点信息。
  • parentComponent:类型为 ?React$Component<any, any>,是一个可选的父组件。在某些情况下,可能需要指定父组件来进行更新操作,但通常为 null。
  • callback:类型为 ?Function,是一个可选的回调函数,在更新完成后会被调用。
function updateContainer(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,// null
  callback: ?Function,// null
): Lane {
  //container 是 FiberRootNode 对象,container.current 指向当前的根 Fiber 节点。这个根 Fiber 节点代表了当前的渲染状态,是整个 React 应用的起点。
  const current = container.current;
  
  //同步直接返回 `SyncLane` = 1。以后开启并发和异步等返回的值就不一样了,目前只有同步这个模式
  //请求 根Fiber 更新车道(lane),用于确定更新的优先级。
  const lane = requestUpdateLane(current);
  
  // 负责执行具体的更新逻辑。它会根据传入的根 Fiber 节点、更新车道、新的 React 元素、容器、父组件和回调函数,进行协调(reconciliation)操作,比较新旧元素的差异,并决定如何更新 DOM 以反映新的元素。
  updateContainerImpl(
    current,// 根fiber
    lane,// 更新车道
    element,// 子节点
    container,// fiberRoot
    parentComponent,// null
    callback,
  );
  return lane;
}

updateContainerImpl

updateContainerImpl 函数是 React 中处理容器更新的具体实现函数,其主要任务是在接收到更新请求后,对更新进行一系列的准备工作,包括标记性能信息、获取上下文、创建更新对象、将更新对象入队,最后调度更新任务以开始执行更新操作。

函数参数含义:

  • rootFiber:类型为 Fiber,代表 React 应用的根 Fiber 节点,是整个更新操作的起始点。
  • lane:类型为 Lane,表示本次更新的优先级车道,用于决定更新任务的执行顺序和优先级。
  • element:类型为 ReactNodeList,是需要更新到容器中的 React 元素或元素列表。
  • container:类型为 OpaqueRoot,是一个不透明的根对象,通常代表 FiberRootNode,包含了整个 React 应用的根节点信息。
  • parentComponent:类型为 ?React$Component<any, any>,是可选的父组件,用于获取上下文。
  • callback:类型为 ?Function,是可选的回调函数,在更新完成后会被调用。
function updateContainerImpl(
  rootFiber: Fiber,// 根节点fiber
  lane: Lane,// 更新优先级车道
  element: ReactNodeList,// 需要更新的react元素
  container: OpaqueRoot,//FiberRootNode
  parentComponent: ?React$Component<any, any>,
  callback: ?Function,
): void {

  //获取当前节点和子节点的上下文
  const context = getContextForSubtree(parentComponent);
  
  if (container.context === null) {
    // 将获取到的上下文赋值给 container.context
    container.context = context;
  } else {
    // 将获取到的上下文赋值给 container.pendingContext
    container.pendingContext = context;
  }

  //创建一个 update 更新对象
  const update = createUpdate(lane);

  // 记录update的载荷信息
  update.payload = {element};

  // 如果有回调信息,保存
  callback = callback === undefined ? null : callback;
  
  if (callback !== null) {
    // 存储callback
    update.callback = callback;
  }
  
  // 将创建好的更新对象 update 加入到根 Fiber 节点的更新队列中。该函数返回根 FiberRootNode 对象。
  // root 为FiberRoot
  const root = enqueueUpdate(rootFiber, update, lane);


  // 调度更新任务
  if (root !== null) {
    
    // 启动一个与当前更新车道相关的计时器,用于记录更新操作的时间。
    // startUpdateTimerByLane(lane);
    
    // 根据更新的优先级车道安排更新任务的执行顺序。
    scheduleUpdateOnFiber(root, rootFiber, lane);
    
    // 处理过渡(transitions)相关的逻辑,确保过渡效果的正确执行。
    // entangleTransitions(root, rootFiber, lane);
  }
  
}

工具函数 createUpdate

createUpdate 函数的主要作用是创建一个更新对象(Update),该对象用于描述 React 组件状态或属性的更新操作。在 React 的更新机制中,更新操作会被封装成一个个 Update 对象,然后被添加到更新队列中,后续会根据这些更新对象来计算新的状态并更新组件。

function createUpdate(lane: Lane): Update<mixed> {
  const update: Update<mixed> = {
    lane,// 优先级车道

    tag: UpdateState,// 更新类型
    payload: null,// payload 用于存储更新操作的具体数据,例如新的状态值、属性值等。在后续的更新过程中,会根据 payload 的内容来计算新的状态。
    callback: null,// 回调,更新完成后执行

    next: null,// next 是一个指向链表中下一个更新对象的指针。在 React 中,更新对象会以链表的形式存储在更新队列中,通过 next 指针可以将多个更新对象连接起来。
  };
  return update;
}

// 表示普通状态的更新,例如hook更新
export const UpdateState = 0;

// 表示替换状态的操作。当使用 ReplaceState 类型的更新时,会直接用新的状态对象替换当前的状态对象,而不是像 UpdateState 那样合并状态。
export const ReplaceState = 1;

// 表示强制更新操作。当调用 forceUpdate 方法(类组件)时,会触发 ForceUpdate 类型的更新。强制更新会绕过状态和属性的浅比较,直接触发组件的重新渲染。
export const ForceUpdate = 2;

// CaptureUpdate 通常与错误边界和捕获阶段的更新有关。在 React 的错误处理机制中,捕获阶段可以捕获子组件抛出的错误,并进行相应的处理。CaptureUpdate 可能用于在捕获阶段触发的状态更新操作。
export const CaptureUpdate = 3;

工具函数之 enqueueUpdate

enqueueUpdate 函数的主要作用是将一个更新对象 update 加入到指定 Fiber 节点的更新队列中。更新队列用于存储组件的状态更新操作,在后续的渲染过程中,React 会根据这些更新操作来计算新的状态。该函数会根据 Fiber 节点的状态和更新阶段,选择不同的方式将更新对象加入队列,并标记从该 Fiber 节点到根节点的更新车道,最后返回根 Fiber 节点。

函数参数含义

  • fiber:类型为 Fiber,代表要加入更新的 Fiber 节点,Fiber 是 React Fiber 架构中的核心数据结构,每个 Fiber 节点对应一个组件实例。
  • update:类型为 Update<State>,是一个更新对象,包含了更新的具体信息,如更新的类型、载荷等。
  • lane:类型为 Lane,表示更新的优先级车道,用于确定更新的执行顺序和优先级。
function enqueueUpdate<State>(
  fiber: Fiber,
  update: Update<State>,
  lane: Lane,
): FiberRoot | null {

  // 根fiber的更新队列
  const updateQueue = fiber.updateQueue;

  // 如果 updateQueue 为空,说明该 fiber 已经被卸载,直接返回 null。
  if (updateQueue === null) {
    //  fiber 被卸载时
    return null;
  }

  // 从 updateQueue 中获取共享队列 sharedQueue。sharedQueue 是一个对象,包含三个属性:interleaved、lanes 和 pending。返回一个对象 {interleaved:null, lanes:0, pending:null}
  const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;

  // 如果 fiber 处于不安全的类渲染阶段
  if (isUnsafeClassRenderPhaseUpdate(fiber)) {
     // pending 永远指向最后一个更新
    const pending = sharedQueue.pending;
    
    // 如果 pending 为空,说明这是第一个更新,需要创建一个循环单链表,将 update.next 指向 update 自己。
    if (pending === null) {
      update.next = update;
    } else {

      // 如果 pending 不为空,取出第一个更新并插入新的更新,使其成为循环单链表的一部分。
      update.next = pending.next;
      pending.next = update;
    }
    
    // 更新 sharedQueue.pending 指向新的 update。
    sharedQueue.pending = update;
    

    // 调用 unsafe_markUpdateLaneFromFiberToRoot 标记更新从 fiber 到根 Fiber,并返回根 Fiber。
    return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
  } else {
    // 调用 enqueueConcurrentClassUpdate 函数处理并发类更新。该函数会根据并发更新的规则将更新对象加入队列,并进行相应的处理
    return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
  }
}

在初始的状态,sharedQueue.pending 为 null。第一次更新的时候 sharedQueue.pending 指向 update,并且 update.next 指向 update 自己,形成一个循环链表。

第二次更新的时候 sharedQueue.pending 更新为指向 update(新传入的),update.next 指向 update1(原来的pending.update),update1.next 指向 update,形成新的循环链表。

 type Update<State> = {
  lane: Lane,
  tag: 0 | 1 | 2 | 3,
  payload: any,
  callback: (() => mixed) | null,
  next: Update<State> | null,
};

工具函数之 enqueueConcurrentClassUpdate

将一个并发类更新对象 update 加入到指定 Fiber 节点的并发更新队列中,并返回该 Fiber 节点对应的根 Fiber 节点。此函数用于处理 React 并发模式下的类组件更新,确保更新操作能被正确地加入队列并关联到根节点

function enqueueConcurrentClassUpdate<State>(
  fiber: Fiber,
  queue: ClassQueue<State>,// 共享队列
  update: ClassUpdate<State>,// 更新对象
  lane: Lane,
): FiberRoot | null {

  // 并发更新队列
  const concurrentQueue: ConcurrentQueue = (queue: any);
  // 并发更新对象
  const concurrentUpdate: ConcurrentUpdate = (update: any);

  // 与前面的enqueueUpdate不同
  // 将转换后的并发更新队列 concurrentQueue 和并发更新对象 concurrentUpdate 加入到指定 Fiber 节点的更新队列中,并指定更新的优先级车道 lane。
  enqueueUpdate(fiber, concurrentQueue, concurrentUpdate, lane);

  // 获取 FiberRoot 节点
  return getRootForUpdatedFiber(fiber);
}
const concurrentQueues: Array<any> = [];
// concurrentQueuesIndex 是一个全局索引,用于记录当前存储位置。
let concurrentQueuesIndex = 0;

// concurrentlyUpdatedLanes 是一个全局变量,用于记录所有并发更新的车道集合。
let concurrentlyUpdatedLanes: Lanes = NoLanes;

function enqueueUpdate(
  fiber: Fiber,
  queue: ConcurrentQueue | null,// 更新队列
  update: ConcurrentUpdate | null,// 更新对象
  lane: Lane,
) {
 // concurrentQueues 是一个全局数组,用于临时存储所有并发更新的相关信息。
  concurrentQueues[concurrentQueuesIndex++] = fiber;
  concurrentQueues[concurrentQueuesIndex++] = queue;
  concurrentQueues[concurrentQueuesIndex++] = update;
  concurrentQueues[concurrentQueuesIndex++] = lane;

  concurrentlyUpdatedLanes = mergeLanes(concurrentlyUpdatedLanes, lane);

// 更新fiber节点的lanes
  fiber.lanes = mergeLanes(fiber.lanes, lane); // fiber.lanes | lanes
  
  const alternate = fiber.alternate;
  if (alternate !== null) {
    // 更新alternate的lanes
    alternate.lanes = mergeLanes(alternate.lanes, lane);
  }
}

工具函数之 getRootForUpdatedFiber 

从一个被更新的 Fiber 节点出发,向上遍历找到对应的根 Fiber 节点(FiberRoot)。

function getRootForUpdatedFiber(sourceFiber: Fiber): FiberRoot | null {
// sourceFiber表示需要查找根节点的起始 Fiber 节点。

  // 检测是否在已卸载的 Fiber 节点上进行更新。
  // detectUpdateOnUnmountedFiber(sourceFiber, sourceFiber);

  // 根fiber
  let node = sourceFiber;
  
  // 父节点
  let parent = node.return;

  // 向上遍历 Fiber 树
  while (parent !== null) {
    // 检测是否在已卸载的 Fiber 节点上进行更新。
    // detectUpdateOnUnmountedFiber(sourceFiber, node);
    
    node = parent;
    parent = node.return;
  }
  return node.tag === HostRoot ? (node.stateNode: FiberRoot) : null;
}

scheduleUpdateOnFiber

其主要功能是为指定的 Fiber 节点安排一个更新任务。该函数会根据当前的渲染状态、更新的优先级以及其他相关条件,对更新任务进行不同的处理,确保更新能够正确、高效地被调度和执行。

  • root:类型为 FiberRoot,代表 React 应用的根节点,是整个 Fiber 树的根。
  • fiber:类型为 Fiber,表示需要进行更新的具体 Fiber 节点。
  • lane:类型为 Lane,代表此次更新的优先级车道。
function scheduleUpdateOnFiber(root: FiberRoot,fiber: Fiber,lane: Lane,) {
   // 检查当前 root 是否处于挂起状态。
// 若根节点 root 是正在进行渲染的根节点 workInProgressRoot,并且渲染因数据未就绪而挂起(workInProgressSuspendedReason === SuspendedOnData),或者根节点有未完成的提交需要取消(root.cancelPendingCommit !== null)
  if (
    (root === workInProgressRoot && 
     workInProgressSuspendedReason === SuspendedOnData) ||
    root.cancelPendingCommit !== null
  ) {
    // 调用 prepareFreshStack 函数为 root 准备一个新的渲染栈
    prepareFreshStack(root, NoLanes);
    
    // 用于标记是否尝试对整个树进行渲染操作,这里设置为 false 表示没有尝试对整个树进行渲染。
    const didAttemptEntireTree = false;

    // 调用 markRootSuspended 函数标记根节点为挂起状态,同时传入相关的渲染车道和是否尝试对整个树进行渲染的标记
    markRootSuspended(
      root,// 要标记的根 Fiber 节点。
      workInProgressRootRenderLanes,// 正在进行渲染的根节点的渲染车道,用于表示渲染的优先级。
      workInProgressDeferredLane,// 正在进行的延迟车道,可能与延迟渲染或更新相关。
      didAttemptEntireTree,// 标记是否尝试对整个树进行渲染。
    );
  }

  // 调用 markRootUpdated 函数,标记根节点有一个挂起的更新,并且记录更新的优先级车道 lane。
  markRootUpdated(root, lane);

  // 检查当前是否处于渲染阶段,并且更新的 root 是正在进行渲染的 root。
  if ((executionContext & RenderContext) !== NoLanes && root === workInProgressRoot) {
    // Track lanes that were updated during the render phase
    // 调用 mergeLanes 函数将当前更新的车道 lane 合并到 workInProgressRootRenderPhaseUpdatedLanes 中,记录渲染阶段的更新车道
    workInProgressRootRenderPhaseUpdatedLanes = mergeLanes(
      workInProgressRootRenderPhaseUpdatedLanes,
      lane,
    );
  } else {
    // 处理正常更新(非渲染阶段)

    if (root === workInProgressRoot) {

    //  如果更新的 root 是正在进行渲染的 root,且当前不在渲染阶段,将更新的车道 lane 合并到 workInProgressRootInterleavedUpdatedLanes 中。
      if ((executionContext & RenderContext) === NoContext) {
        workInProgressRootInterleavedUpdatedLanes = mergeLanes(
          workInProgressRootInterleavedUpdatedLanes,
          lane,
        );
      }
      
    // 如果 root 处于延迟挂起状态,再次标记 root 为挂起状态。
      if (workInProgressRootExitStatus === RootSuspendedWithDelay) {

        const didAttemptEntireTree = false;
        markRootSuspended(
          root,
          workInProgressRootRenderLanes,
          workInProgressDeferredLane,
          didAttemptEntireTree,
        );
      }
    }

    // 调用 ensureRootIsScheduled 函数确保根节点被正确调度。
    ensureRootIsScheduled(root);

  }
}

ensureRootIsScheduled

ensureRootIsScheduled 函数的主要作用是确保 FiberRoot 节点被正确调度。它会将 FiberRoot 节点添加到调度列表中,标记可能存在待处理的同步工作,安排微任务来处理根节点的调度,并且根据配置决定是否在微任务期间为根节点安排任务。

let firstScheduledRoot: FiberRoot | null = null;
let lastScheduledRoot: FiberRoot | null = null;
let didScheduleMicrotask: boolean = false;

function ensureRootIsScheduled(root: FiberRoot): void {
  // 首先检查 root 是否已经在调度列表中。
  // 如果 root 等于 lastScheduledRoot 或者 root.next 不为 null,说明该根节点已经被调度,直接跳过后续添加操作。
  if (root === lastScheduledRoot || root.next !== null) {
    // Fast path. This root is already scheduled.
  } else {

    // 若 lastScheduledRoot 为 null,表示调度列表为空,将 firstScheduledRoot 和 lastScheduledRoot 都设置为 root,即该根节点成为调度列表中的第一个也是最后一个节点。
    if (lastScheduledRoot === null) {
      firstScheduledRoot = lastScheduledRoot = root;
    } else {
      // 若 lastScheduledRoot 不为 null,将 lastScheduledRoot.next 设置为 root,并将 lastScheduledRoot 更新为 root,即将该根节点添加到调度列表的末尾。
      lastScheduledRoot.next = root;
      lastScheduledRoot = root;
    }
  }

  // 将 mightHavePendingSyncWork 标记为 true,表示可能存在待处理的同步工作。这可能会影响后续的调度决策,例如在某些情况下需要优先处理同步工作。
  mightHavePendingSyncWork = true;

  // 检查 didScheduleMicrotask 标志,如果为 false,说明还没有安排微任务来处理根节点的调度。
  if (!didScheduleMicrotask) {
    // 将 didScheduleMicrotask 设置为 true,表示已经安排了微任务。
    didScheduleMicrotask = true;
    // 用 scheduleImmediateTask 函数,安排一个立即执行的微任务,执行 processRootScheduleInMicrotask 函数,该函数可能会处理根节点的调度逻辑。
    scheduleImmediateTask(processRootScheduleInMicrotask);
  }
  
// 检查 enableDeferRootSchedulingToMicrotask 配置项,如果为 false,表示不延迟根节点的调度到微任务中。
  if (!enableDeferRootSchedulingToMicrotask) {
    // 调用 scheduleTaskForRootDuringMicrotask 函数,在微任务期间为根节点安排任务,now() 函数返回当前时间,可能用于确定任务的调度时间。
    scheduleTaskForRootDuringMicrotask(root, now());
  }
}

scheduleImmediateTask(processRootScheduleInMicrotask) 立即执行的微任务。即当当前宏任务执行完毕后立即执行。

工具函数之 scheduleImmediateTask

scheduleImmediateTask 函数的作用是安排一个立即执行的任务。它会根据当前环境是否支持微任务(microtasks)来选择合适的执行方式

function scheduleImmediateTask(cb: () => mixed) {
  // 参数cb为立即执行的任务

  // 支持微任务
  if (supportsMicrotasks) {

    // 把传入的回调函数安排到微任务队列中。当宏任务处理完后在再次执行这里
    scheduleMicrotask(() => {

      // 获取当前的执行上下文。
      const executionContext = getExecutionContext();

      // 检查当前执行上下文是否处于渲染(RenderContext)或提交(CommitContext)阶段。
      if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {

        // 处于渲染阶段或提交阶段, 安排回调函数的执行
        Scheduler_scheduleCallback(ImmediateSchedulerPriority, cb);
        return;
      }
      // 执行回调函数
      cb();
    });
  } else {
    // If microtasks are not supported, use Scheduler.
    // 安排回调函数的执行
    Scheduler_scheduleCallback(ImmediateSchedulerPriority, cb);
  }
}

 const scheduleMicrotask: any =
  typeof queueMicrotask === 'function'
    ? queueMicrotask
    : typeof localPromise !== 'undefined'
      ? callback =>
          localPromise.resolve(null).then(callback).catch(handleErrorInNextTick)
      : scheduleTimeout; // TODO: Determine the best fallback here.
const localPromise = typeof Promise === 'function' ? Promise : undefined;

Window:queueMicrotask() 方法 - Web API | MDN

 工具函数之 scheduleCallback

unstable_scheduleCallback 函数是 React 调度机制中的核心函数,用于根据任务的优先级和延迟时间,将任务分配到不同的队列(taskQueue 或 timerQueue)中,并调度相应的宿主回调(如浏览器事件循环)来执行任务。其核心逻辑包括:

  1. 根据优先级和延迟时间计算任务的开始时间和过期时间。
  2. 将延迟任务加入 timerQueue,非延迟任务加入 taskQueue
  3. 调度宿主超时(requestHostTimeout)或回调(requestHostCallback)以触发任务执行。
const scheduleCallback = Scheduler.unstable_scheduleCallback;
// 待执行任务队列
var taskQueue: Array<Task> = [];

// 定时器任务队列
var timerQueue: Array<Task> = [];

function unstable_scheduleCallback(
  priorityLevel: PriorityLevel,// 任务优先级
  callback: Callback,// 回调函数
  options?: {delay: number},// delay 延迟任务的毫秒
): Task {
  // 
  var currentTime = getCurrentTime();

  // 定义任务开始时间
  var startTime;
  if (typeof options === 'object' && options !== null) {
    var delay = options.delay;
    if (typeof delay === 'number' && delay > 0) {
      // 任务开始时间, 当前时间加上延迟时间
      startTime = currentTime + delay;
    } else {
      startTime = currentTime;
    }
  } else {
    startTime = currentTime;
  }

  // 根据优先级设置超时时间
  var timeout;

  // 车道优先级
  switch (priorityLevel) {
    case ImmediatePriority:// 立即超市
      // Times out immediately
      timeout = -1;
      break;
    case UserBlockingPriority:
      // Eventually times out
      timeout = userBlockingPriorityTimeout;
      break;
    case IdlePriority:// 永不超时
      // Never times out
      timeout = maxSigned31BitInt;
      break;
    case LowPriority:
      // Eventually times out
      timeout = lowPriorityTimeout;
      break;
    case NormalPriority:
    default:
      // Eventually times out
      timeout = normalPriorityTimeout;
      break;
  }

  // 过期时间 
  var expirationTime = startTime + timeout;

  // 创建新任务对象
  var newTask: Task = {
    id: taskIdCounter++, // 任务ID
    callback,// 回调函数
    priorityLevel,// 任务优先级
    startTime,// 开始时间
    expirationTime,// 过期时间
    sortIndex: -1,// 排序索引
  };

  // 根据开始时间加入不同队列

  // 延迟任务(startTime > currentTime)
  if (startTime > currentTime) {
    // This is a delayed task.
    // 将任务的排序索引设置为开始时间。
    newTask.sortIndex = startTime;

    // 将任务加入 timerQueue(延迟任务队列)。
    push(timerQueue, newTask);

    // 如果 taskQueue 为空 且 当前任务是 timerQueue 中最早的延迟任务
    if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
      // All tasks are delayed, and this is the task with the earliest delay.
      // 标记是否已经安排了一个主机超时任务。
      if (isHostTimeoutScheduled) {
        // Cancel an existing timeout.
        cancelHostTimeout();
      } else {
        isHostTimeoutScheduled = true;
      }
      // Schedule a timeout.
      // requestHostTimeout设置一个定时器任务
      // handleTimeout 是超时后需要执行的回调函数,通常用于处理延迟任务队列中的任务。
      // startTime - currentTime 表示从当前时间到新任务开始执行的时间间隔,即需要等待的时间。
      requestHostTimeout(handleTimeout, startTime - currentTime);
    }
  } else {
    // 非延迟任务(startTime <= currentTime)

   //将任务的排序索引设置为过期时间。
    newTask.sortIndex = expirationTime;
    
    // 将任务加入 taskQueue(待执行任务队列)。
    push(taskQueue, newTask);

    // 如果没有正在调度的宿主回调,并且当前没有正在执行的工作,则安排一个宿主回调。
    if (!isHostCallbackScheduled && !isPerformingWork) {
      isHostCallbackScheduled = true;
      requestHostCallback();
    }
  }

  return newTask;
}

requestHostCallback 函数是 React 调度系统中与宿主环境(通常指浏览器)交互的关键函数之一,其主要作用是请求宿主环境调度执行任务。该函数会检查消息循环是否正在运行,如果没有运行,则启动消息循环,并安排执行任务直到达到截止时间。 

function requestHostCallback() {
  if (!isMessageLoopRunning) {
    isMessageLoopRunning = true;
    schedulePerformWorkUntilDeadline();
  }
}

flushWork

flushWork 函数是 React 任务调度系统中的核心执行函数,主要用于触发任务的实际执行。它会在任务队列准备好后,调用 workLoop 函数循环处理任务,直到没有可执行的任务或需要向宿主环境让步。同时,该函数还负责管理任务执行的状态标记(如是否正在执行、是否需要调度宿主回调等),确保任务执行流程的正确性和完整性。

function flushWork(initialTime: number) {

  // 将 “是否已安排宿主回调” 的标记置为 false。这意味着本次任务执行完毕后,下次再有任务时需要重新调度宿主回调(如通过 requestHostCallback)。
  isHostCallbackScheduled = false;

  // 如果存在已安排的超时任务(如延迟执行的定时器任务),但当前需要执行的是立即任务(非延迟任务),则取消超时任务。
  if (isHostTimeoutScheduled) {
    // We scheduled a timeout but it's no longer needed. Cancel it.
    isHostTimeoutScheduled = false;
    cancelHostTimeout();
  }

  // 用于防止任务执行过程中被重复调度,确保同一时间只有一个任务循环在运行。
  isPerformingWork = true;
  // previousPriorityLevel 保存当前的优先级级别
  const previousPriorityLevel = currentPriorityLevel;
  
  try {
      return workLoop(initialTime);
  } finally {
    
  // 清空当前正在处理的任务,避免内存泄漏。
    currentTask = null;
  // 恢复之前保存的优先级级别。
    currentPriorityLevel = previousPriorityLevel;
  // 标记任务执行结束,允许下次调度。
    isPerformingWork = false;
  }
}

workLoop

workLoop 函数是一个任务调度的核心循环,它负责在给定的初始时间内,持续从任务队列(taskQueue)中取出任务并执行,直到满足某些条件才停止。同时,它还会处理定时器队列(timerQueue)中的任务,确保任务能在合适的时间执行。


function workLoop(initialTime: number) {
  let currentTime = initialTime;
  advanceTimers(currentTime);

  // 使用 peek 函数从任务队列中取出第一个任务赋值给 currentTask,没有则为null
  currentTask = peek(taskQueue);

  // 进入 while 循环,只要任务队列中有任务,并且调度器没有因为调试模式而暂停,就会继续循环。
  while (
    currentTask !== null &&
    !(enableSchedulerDebugging && isSchedulerPaused)
  ) {

    // 如果当前任务的过期时间大于当前时间,并且需要向宿主环境让步(例如浏览器需要处理其他事件),则跳出循环。
    if (currentTask.expirationTime > currentTime && shouldYieldToHost()) {
      break;
    }

    // 取出当前任务的回调函数 callback。
    const callback = currentTask.callback;
    
    if (typeof callback === 'function') {
     // 将当前任务的回调函数置为 null,避免重复执行。
      currentTask.callback = null;

      // 设置当前的优先级为当前任务的优先级。
      currentPriorityLevel = currentTask.priorityLevel;

      // 判断当前任务是否已经过期。
      const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;

      // 执行回调函数 callback,并传入任务是否过期的标志,得到返回的延续回调函数 continuationCallback。
      const continuationCallback = callback(didUserCallbackTimeout);
      currentTime = getCurrentTime();

      // 处理延续回调函数
      if (typeof continuationCallback === 'function') {
       // 如果延续回调函数是一个函数,则将其赋值给当前任务的回调函数,再次推进定时器,然后返回 true 表示还有额外的工作。
        currentTask.callback = continuationCallback;
        advanceTimers(currentTime);
        return true;
      } else {
        // 如果延续回调函数不是一个函数,并且当前任务是任务队列的第一个任务,则从任务队列中移除该任务,再次推进定时器。
        if (currentTask === peek(taskQueue)) {
          pop(taskQueue);
        }
        advanceTimers(currentTime);
      }
    } else {
      // 移除该任务
      pop(taskQueue);
    }

    // 取出下一个任务
    currentTask = peek(taskQueue);
  }
  // 判断是否还有任务
  if (currentTask !== null) {
    return true;
  } else {
    // 如果任务队列中没有任务,检查定时器队列中是否有任务。如果有,使用 requestHostTimeout 函数在合适的时间调用 handleTimeout 函数来处理定时器任务,然后返回 false 表示没有额外的工作。
    const firstTimer = peek(timerQueue);
    if (firstTimer !== null) {
      requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
    }
    return false;
  }
}
// Tasks are stored on a min heap
var taskQueue: Array<Task> = [];
var timerQueue: Array<Task> = [];
//opaque type 是 Flow 特有的不透明类型声明。不透明类型意味着外部只能知道它的名字,而不能直接访问其内部结构
export opaque type Task = {
  id: number,
  callback: Callback | null,
  priorityLevel: PriorityLevel,
  startTime: number,
  expirationTime: number,
  sortIndex: number,
  isQueued?: boolean,
};
  • id: number:每个任务都有一个唯一的 id,类型为 number,用于标识任务。
  • callback: Callback | nullcallback 是任务要执行的回调函数,类型为 Callback 或者 nullCallback 应该是在其他地方定义的一种函数类型。
  • priorityLevel: PriorityLevelpriorityLevel 表示任务的优先级,PriorityLevel 应该是一个自定义的类型,用于表示不同的优先级等级。
  • startTime: numberstartTime 是任务的开始时间,类型为 number,通常可能是一个时间戳。
  • expirationTime: numberexpirationTime 是任务的过期时间,类型为 number,同样可能是一个时间戳。当任务超过这个时间还未执行,可能会有相应的处理逻辑。
  • sortIndex: numbersortIndex 是用于排序的索引,类型为 number。在任务调度时,可能会根据这个索引对任务进行排序。
  • isQueued?: booleanisQueued 是一个可选属性,类型为 boolean,用于表示任务是否已经被加入到任务队列中。 

工具函数之 performWorkUntilDeadline

performWorkUntilDeadline 函数的主要作用是在浏览器的消息循环中持续执行工作任务,直到没有更多的工作或者达到某个截止时间。它会在当前浏览器任务中不断尝试刷新工作,若还有剩余工作则安排下一次消息事件继续执行,若没有剩余工作则停止消息循环。

const performWorkUntilDeadline = () => {
  // isMessageLoopRunning 是一个布尔标志,用于判断消息循环是否正在运行。只有当消息循环正在运行时,才会执行后续的工作。
  if (isMessageLoopRunning) {
    const currentTime = getCurrentTime();
    // Keep track of the start time so we can measure how long the main thread
    // has been blocked.
    startTime = currentTime;
// hasMoreWork 初始化为 true,用于标记是否还有更多的工作需要执行。
    let hasMoreWork = true;
    try {
      // 调用 flushWork(currentTime) 函数来尝试刷新工作。flushWork 函数会根据当前时间处理调度任务,并返回一个布尔值,表示是否还有剩余的工作。
      hasMoreWork = flushWork(currentTime);
    } finally {
      if (hasMoreWork) {
     // 如果 hasMoreWork 为 true,说明还有工作未完成,调用 schedulePerformWorkUntilDeadline() 函数安排下一次消息事件,以便继续执行剩余的工作。
        schedulePerformWorkUntilDeadline();
      } else {
        // 如果 hasMoreWork 为 false,说明所有工作都已完成,将 isMessageLoopRunning 标志设置为 false,表示消息循环停止运行。
        isMessageLoopRunning = false;
      }
    }
  }
};

const getCurrentTime =
  // $FlowFixMe[method-unbinding]
  typeof performance === 'object' && typeof performance.now === 'function'
    ? () => performance.now()
    : () => Date.now();

performance.now()‌:基准点是页面的导航开始时间(即页面加载时刻),返回的是当前时间距离页面加载的时间差。它的值通常较小,且不容易受到系统时钟调整的影响‌。

Date.now()‌:基准点是1970年1月1日00:00:00 UTC(Unix纪元),返回的是自该时刻以来的毫秒数。它的值是一个非常大的数字,可能会受到系统时间变更的影响‌。

工具函数之 schedulePerformWorkUntilDeadline

实现 schedulePerformWorkUntilDeadline 函数,该函数的作用是安排 performWorkUntilDeadline 函数尽快执行。代码会根据当前环境支持的特性,选择不同的异步调度方式来实现这一功能,确保在不同环境下都能高效地调度任务。

使用优先顺序: setImmediate > MessageChannel > setTimeout

let schedulePerformWorkUntilDeadline;
if (typeof localSetImmediate === 'function') {
  schedulePerformWorkUntilDeadline = () => {
    localSetImmediate(performWorkUntilDeadline);
  };
} else if (typeof MessageChannel !== 'undefined') {
  const channel = new MessageChannel();
  const port = channel.port2;
  channel.port1.onmessage = performWorkUntilDeadline;
  
  schedulePerformWorkUntilDeadline = () => {
    port.postMessage(null);
  };
} else {
  // We should only fallback here in non-browser environments.
  schedulePerformWorkUntilDeadline = () => {
    // $FlowFixMe[not-a-function] nullable value
    localSetTimeout(performWorkUntilDeadline, 0);
  };
}
const localSetTimeout = typeof setTimeout === 'function' ? setTimeout : null;

const localClearTimeout =
  typeof clearTimeout === 'function' ? clearTimeout : null;

const localSetImmediate =
  typeof setImmediate !== 'undefined' ? setImmediate : null; // IE and Node.js + jsdom

setImmediate 不再推荐使用。用来把一些需要长时间运行的操作放在一个回调函数里,在浏览器完成后面的其他语句后,就立刻执行这个回调函数。

https://developer.mozilla.org/zh-CN/docs/Web/API/Window/setImmediate

https://developer.mozilla.org/zh-CN/docs/Web/API/MessageChannel

例子:关于MessageChannel的使用

// 创建一个新的消息通道
const channel = new MessageChannel();

// port1 和 port2 是两个相互连接的端口
const port1 = channel.port1;
const port2 = channel.port2;

// 在 port1 上设置消息监听
port1.onmessage = (event) => {
  console.log('port1 收到消息:', event.data);
};

// 通过 port2 发送消息
port2.postMessage('Hello from port2!');

// 也可以在 port2 上设置监听
port2.onmessage = (event) => {
  console.log('port2 收到消息:', event.data);
};

// 通过 port1 发送消息
port1.postMessage('Hello from port1!');

例子:与iframe的通信

const iframe = document.querySelector('iframe');
const channel = new MessageChannel();

// 将 port2 传递给 iframe
iframe.contentWindow.postMessage('init', '*', [channel.port2]);

// 在主窗口使用 port1
channel.port1.onmessage = (event) => {
  console.log('来自 iframe 的消息:', event.data);
};

工具函数之 peek

取出堆顶元素,没有则返回null。

function peek<T: Node>(heap: Heap<T>): T | null {
  return heap.length === 0 ? null : heap[0];
}
type Heap<T: Node> = Array<T>;
type Node = {
  id: number,
  sortIndex: number,
  ...
};

 工具函数之 push

将一个node参数加入到数组中,并重新排序。

function push<T: Node>(heap: Heap<T>, node: T): void {
  const index = heap.length;
  heap.push(node);
  // 重新调整堆
  siftUp(heap, node, index);
}

工具函数之 pop 

pop 函数用于从最小堆(小顶堆)中移除并返回堆顶元素(即堆中最小的元素),同时维护堆的性质。小顶堆是一种特殊的完全二叉树,其中每个节点的值都小于或等于其子节点的值。该函数会先检查堆是否为空,如果为空则返回 null,否则移除堆顶元素,并将堆的最后一个元素移动到堆顶,然后通过 siftDown 操作重新调整堆,使其恢复最小堆的性质。

function pop<T: Node>(heap: Heap<T>): T | null {
  if (heap.length === 0) {
    return null;
  }

    // 取出第一个元素
  const first = heap[0];
    
    // 删除并返回数组的最后一个元素,改变数组长度
  const last = heap.pop();

// 如何删除的 元素不是第一个元素,便将最后一个元素设置为第一个元素
  if (last !== first) {
    
    heap[0] = last;
    
    // 重新调整堆
    siftDown(heap, last, 0);
  }
  return first;
}

工具函数之 siftUp 

实现了最小堆(小顶堆)中的 siftUp 操作。最小堆是一种特殊的完全二叉树,树中每个节点的值都小于或等于其子节点的值。siftUp 操作主要用于在向堆中插入新元素或者更新某个元素的值后,通过不断将该元素与其父节点比较并交换位置,使堆重新满足最小堆的性质。

function siftUp<T: Node>(heap: Heap<T>, node: T, i: number): void {
  let index = i;
  while (index > 0) {
    
    // 无符号右移
    const parentIndex = (index - 1) >>> 1;
    const parent = heap[parentIndex];
    if (compare(parent, node) > 0) {
      // The parent is larger. Swap positions.
      heap[parentIndex] = node;
      heap[index] = parent;
      index = parentIndex;
    } else {
      // The parent is smaller. Exit.
      return;
    }
  }
}

siftDown 函数实现了最小堆(小顶堆)的下沉操作。在最小堆这种数据结构中,每个节点的值都小于或等于其子节点的值。当堆中的某个节点的值发生变化(通常是增大),或者新插入一个节点到堆的底部后需要将其调整到合适位置时,就需要使用下沉操作来维护堆的性质。该函数接收一个堆数组 heap、一个节点 node 以及该节点在堆中的初始索引 i,通过不断比较节点与其子节点的大小,并进行交换,将节点下沉到合适的位置。 

function siftDown<T: Node>(heap: Heap<T>, node: T, i: number): void {
  let index = i;
  const length = heap.length;

 // 无符号右移
  const halfLength = length >>> 1;
  while (index < halfLength) {
    const leftIndex = (index + 1) * 2 - 1;
    const left = heap[leftIndex];
    const rightIndex = leftIndex + 1;
    const right = heap[rightIndex];

    // If the left or right node is smaller, swap with the smaller of those.
    if (compare(left, node) < 0) {
      if (rightIndex < length && compare(right, left) < 0) {
        heap[index] = right;
        heap[rightIndex] = node;
        index = rightIndex;
      } else {
        heap[index] = left;
        heap[leftIndex] = node;
        index = leftIndex;
      }
    } else if (rightIndex < length && compare(right, node) < 0) {
      heap[index] = right;
      heap[rightIndex] = node;
      index = rightIndex;
    } else {
      // Neither child is smaller. Exit.
      return;
    }
  }
}

工具函数之 compare 

function compare(a: Node, b: Node) {
  // Compare sort index first, then task id.
  const diff = a.sortIndex - b.sortIndex;
  return diff !== 0 ? diff : a.id - b.id;
}

工具函数之 advanceTimers 

advanceTimers 函数的主要功能是根据当前时间 currentTime 对定时器队列 timerQueue 进行处理。它会遍历定时器队列中的任务,根据任务的状态(回调函数是否为空、是否到达开始时间)来决定是移除任务、将任务从定时器队列转移到任务队列 taskQueue 中,还是停止处理。

function advanceTimers(currentTime: number) {
  // 取出定时队列中的第一个任务
  let timer = peek(timerQueue);

    //遍历定时器队列
  while (timer !== null) {
    if (timer.callback === null) {
      // 移除第一个任务
      pop(timerQueue);
    } else if (timer.startTime <= currentTime) {
      // 过期了 移除任务
      pop(timerQueue);
      timer.sortIndex = timer.expirationTime;
      // 添加任务
      push(taskQueue, timer);
    } else {
      // Remaining timers are pending.
      return;
    }
    // 取出任务
    timer = peek(timerQueue);
  }
}

工具函数之 requestHostTimeout

function requestHostTimeout(
  callback: (currentTime: number) => void,
  ms: number,
) {
  // 延时执行setTimeout
  taskTimeoutID = localSetTimeout(() => {
    callback(getCurrentTime());
  }, ms);
}

processRootScheduleInMicrotask

processRootScheduleInMicrotask 函数是 React 调度系统中处理微任务阶段根节点调度的核心函数。其主要作用是在微任务中遍历所有已调度的根节点(FiberRoot),为每个根节点安排任务,并根据任务状态决定是否保留根节点在调度列表中。同时,该函数会处理同步过渡车道和同步工作,确保高优先级任务(如同步任务)能及时执行。

function processRootScheduleInMicrotask() {
 // 标记是否已调度微任务(初始为 false)
  didScheduleMicrotask = false;

  // 标记是否存在待处理的同步工作
  mightHavePendingSyncWork = false;

  // 存储同步过渡车道,初始化为 NoLanes。
  let syncTransitionLanes = NoLanes;

  // 处理过渡车道, 过渡车道:用于标记需要紧急处理的状态过渡(如用户输入触发的更新)。
  if (currentEventTransitionLane !== NoLane) {
    // 判断是否需要提前处理过渡任务(如同步任务优先执行)。
    if (shouldAttemptEagerTransition()) {
      syncTransitionLanes = currentEventTransitionLane;
    }
    // 重置当前事件过渡车道
    currentEventTransitionLane = NoLane;
  }

  const currentTime = now();

  // 记录前一个节点
  let prev = null;
  
  // 从调度列表头部开始遍历
  let root = firstScheduledRoot;//firstScheduledRoot就是fiberRoot

  // 遍历所有已调度的根节点。
  while (root !== null) {
    const next = root.next;// next = null
    // 对于每个根节点,调用 scheduleTaskForRootDuringMicrotask 函数为其安排任务,得到 nextLanes。
    const nextLanes = scheduleTaskForRootDuringMicrotask(root, currentTime);
    
    if (nextLanes === NoLane) {
      // 根节点已无待处理任务,从调度列表中移除
      root.next = null;
      if (prev === null) {
        firstScheduledRoot = next;// 更新列表头部
      } else {
        prev.next = next;
      }
      if (next === null) {
        lastScheduledRoot = prev;// 更新列表尾部
      }
      
    } else {
      // This root still has work. Keep it in the list.
      prev = root;

      if (syncTransitionLanes !== NoLanes || includesSyncLane(nextLanes)) {
        mightHavePendingSyncWork = true;
      }
    }
    root = next;
  }

   // 刷新同步工作
  flushSyncWorkAcrossRoots_impl(syncTransitionLanes, false);// syncTransitionLanes = 0
}

flushSyncWorkAcrossRoots_impl

flushSyncWorkAcrossRoots_impl 函数的主要目的是在多个 FiberRoot 上刷新同步工作。它会遍历所有已调度的 FiberRoot,检查每个根节点是否有等待执行的同步工作,如果有则调用 performSyncWorkOnRoot 函数执行这些同步工作。

function flushSyncWorkAcrossRoots_impl(
  syncTransitionLanes: Lanes | Lane,
  onlyLegacy: boolean,// false
) {
  // isFlushingWork 是一个标志,用于防止函数重入。如果当前已经在刷新工作,函数会直接返回,避免重复操作。
  if (isFlushingWork) {
    return;
  }

  // mightHavePendingSyncWork 用于快速判断是否可能存在待执行的同步工作。如果为 false,则说明没有同步工作需要处理,函数直接返回。
  if (!mightHavePendingSyncWork) {
    // Fast path. There's no sync work to do.
    return;
  }

  // didPerformSomeWork 用于记录本次循环中是否执行了同步工作。
  let didPerformSomeWork;

  // isFlushingWork 置为 true,表示开始进行刷新工作。
  isFlushingWork = true;

  // do-while 循环会持续执行,直到本次循环中没有执行任何同步工作。
  do {
    didPerformSomeWork = false;
    let root = firstScheduledRoot;
    
    while (root !== null) {
      
      if (onlyLegacy && (disableLegacyMode || root.tag !== LegacyRoot)) {
        // Skip non-legacy roots.
      } else {
        if (syncTransitionLanes !== NoLanes) {
          const nextLanes = getNextLanesToFlushSync(root, syncTransitionLanes);
          if (nextLanes !== NoLanes) {
            // This root has pending sync work. Flush it now.
            didPerformSomeWork = true;
            performSyncWorkOnRoot(root, nextLanes);
          }
        } else {
          const workInProgressRoot = getWorkInProgressRoot();
          const workInProgressRootRenderLanes =
            getWorkInProgressRootRenderLanes();
          const nextLanes = getNextLanes(
            root,
            root === workInProgressRoot
            ? workInProgressRootRenderLanes
            : NoLanes,
          );
          if (
            includesSyncLane(nextLanes) &&
            !checkIfRootIsPrerendering(root, nextLanes)
          ) {
            // This root has pending sync work. Flush it now.
            didPerformSomeWork = true;
            performSyncWorkOnRoot(root, nextLanes);
          }
        }
      }
      root = root.next;
    }
  } while (didPerformSomeWork);
  // 将 isFlushingWork 置为 false,表示刷新工作结束。
  isFlushingWork = false;
}

performSyncWorkOnRoot

performSyncWorkOnRoot 函数是 React 中用于执行同步工作的入口函数,主要处理那些不经过调度器(Scheduler)的同步任务。它会先尝试刷新被动副作用(passive effects),根据刷新结果决定是否继续执行后续的同步工作,最后调用 performWorkOnRoot 函数来真正执行根节点上的同步工作。

参数说明

  • root: FiberRoot:表示 React 应用的根节点,代表整个 React 应用的 Fiber 树的根。
  • lanes: Lanes:渲染优先级车道,用于标识当前同步工作的优先级。
    function performSyncWorkOnRoot(root: FiberRoot, lanes: Lanes) {
    // 调用flushPassiveEffects函数尝试刷新被动副作用,被动副作用通常是指 useEffect 等钩子中异步执行的副作用操作。
      const didFlushPassiveEffects = flushPassiveEffects();
    
      // 如果 didFlushPassiveEffects 为 true,说明成功刷新了被动副作用,此时函数直接返回 null,退出到根调度器的外部工作循环,以便重新计算优先级。
      if (didFlushPassiveEffects) {
        return null;
      }
    
      // 设置 forceSync 为 true,表示强制进行同步渲染。
      const forceSync = true;
      
      // 执行根节点上的同步工作
      performWorkOnRoot(root, lanes, forceSync);
    }
    

    performWorkOnRoot

    performWorkOnRoot 函数是 React 渲染流程中的关键函数,主要负责在指定的 FiberRoot 上执行渲染工作。它会根据不同的条件判断是否使用时间切片(time-slicing)机制,然后调用相应的渲染函数进行渲染,最后确保根节点被正确调度。

    参数说明

    • root: FiberRoot:React 应用的根节点,代表整个 React 应用的 Fiber 树的根。
    • lanes: Lanes:渲染优先级车道,用于标识当前渲染任务的优先级。
    • forceSync: boolean:一个布尔值,指示是否强制同步渲染。
      function performWorkOnRoot(
        root: FiberRoot,
        lanes: Lanes,
        forceSync: boolean,// true
      ): void {
      
        // executionContext 是一个表示当前执行上下文的变量,RenderContext 和 CommitContext 分别代表渲染上下文和提交上下文。通过按位与操作检查当前是否处于渲染或提交上下文中,如果是,则抛出错误,因为不应该在已经工作的情况下再次调用此函数。
        if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
          throw new Error('Should not already be working.');
        }
        
      
      // 判断是否使用时间切片
        const shouldTimeSlice =
          // !forceSync:如果不是强制同步渲染。
          (!forceSync &&
           // !includesBlockingLane(lanes):当前渲染任务的优先级车道中不包含阻塞车道。
            !includesBlockingLane(lanes) &&
           // !includesExpiredLane(root, lanes):根节点的渲染任务中不包含过期的车道。
            !includesExpiredLane(root, lanes)) ||
       // enableSiblingPrerendering:是否启用了兄弟节点预渲染功能。
          // checkIfRootIsPrerendering(root, lanes):检查根节点是否正在预渲染。
          (enableSiblingPrerendering && checkIfRootIsPrerendering(root, lanes));
      
      
        // 执行渲染
        let exitStatus = shouldTimeSlice
          // 调用 renderRootConcurrent 函数进行并发渲染,即使用时间切片机制,将渲染工作分成多个小任务,在浏览器的空闲时间执行,避免阻塞主线程。
          ? renderRootConcurrent(root, lanes)
          // 调用 renderRootSync 函数进行同步渲染,一次性完成渲染工作。
          : renderRootSync(root, lanes, true);
      
        
        let renderWasConcurrent = shouldTimeSlice;
      
        // 省略代码。。。 处理渲染结果
      
        // 确保根节点被调度
          ensureRootIsScheduled(root);
      }
      
      do {
        // 当 exitStatus 为 RootInProgress 时,表示渲染阶段仍在进行中。
        if (exitStatus === RootInProgress) {
          // 启用了兄弟节点预渲染(enableSiblingPrerendering)
          // 当前根节点正在预渲染(workInProgressRootIsPrerendering)
          // 且不使用时间切片(!shouldTimeSlice)
          if (
            enableSiblingPrerendering &&
            workInProgressRootIsPrerendering &&
            !shouldTimeSlice
          ) {
        
            const didAttemptEntireTree = false;
            markRootSuspended(root, lanes, NoLane, didAttemptEntireTree);
          }
          // 跳出循环
          break;
      
          // 当 exitStatus 为 RootDidNotComplete 时,表示渲染过程在未完成整个树的情况下退出,这可能是由于特殊情况导致的。
        } else if (exitStatus === RootDidNotComplete) {
        
          const didAttemptEntireTree = !workInProgressRootDidSkipSuspendedSiblings;
          // 将根节点标记为暂停状态。
          markRootSuspended(root, lanes, NoLane, didAttemptEntireTree);
          
        } else {
         
          const finishedWork: Fiber = (root.current.alternate: any);
          if (
            renderWasConcurrent &&
            // 对比前后是否一致
            !isRenderConsistentWithExternalStores(finishedWork)
          ) {
           // 执行同步渲染
            exitStatus = renderRootSync(root, lanes, false);
            // We assume the tree is now consistent because we didn't yield to any
            // concurrent events.
            renderWasConcurrent = false;
            // 跳出循环,重新判断exitStatus
            continue;
          }
      
          // Check if something threw
          if (
            (disableLegacyMode || root.tag !== LegacyRoot) &&
            exitStatus === RootErrored // 渲染出错
          ) {
            const lanesThatJustErrored = lanes;
            // 获取同步重试车道
            const errorRetryLanes = getLanesToRetrySynchronouslyOnError(
              root,
              lanesThatJustErrored,
            );
      
            // 重试车道
            if (errorRetryLanes !== NoLanes) {
              lanes = errorRetryLanes;
              // 执行恢复错误
              exitStatus = recoverFromConcurrentError(
                root,
                lanesThatJustErrored,
                errorRetryLanes,
              );
              renderWasConcurrent = false;
              // Need to check the exit status again.
              if (exitStatus !== RootErrored) {
                continue;
              } 
            }
          }
      
          // 致命错误
          if (exitStatus === RootFatalErrored) {
            // 准备新的执行栈
            prepareFreshStack(root, NoLanes);
            const didAttemptEntireTree = true;
            markRootSuspended(root, lanes, NoLane, didAttemptEntireTree);
            break;
          }
      
         // 调用 finishConcurrentRender 函数完成并发渲染。
          finishConcurrentRender(root, exitStatus, finishedWork, lanes);
        }
        break;
      } while (true);

      renderRootConcurrent

      renderRootConcurrent 函数是 React 中进行并发渲染的核心函数。它负责处理根节点的渲染工作,包括判断是否需要重新准备渲染栈、处理不同的暂停原因(如数据暂停、错误暂停等)、执行工作循环以及处理渲染完成后的操作。

      function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
        const prevExecutionContext = executionContext;
        executionContext |= RenderContext;
        // 调度器
        const prevDispatcher = pushDispatcher(root.containerInfo);
        // 异步调度器
        const prevAsyncDispatcher = pushAsyncDispatcher();
      
        if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
      
          workInProgressTransitions = getTransitionsForLanes(root, lanes);
      
          resetRenderTimer();
          prepareFreshStack(root, lanes);
        } else {
          workInProgressRootIsPrerendering = checkIfRootIsPrerendering(root, lanes);
        }
      
      // 省略代码 do 。。。while
      
        // 重置
        resetContextDependencies();
      
        // 恢复之前的调度器与执行上下文
        popDispatcher(prevDispatcher);
        popAsyncDispatcher(prevAsyncDispatcher);
        executionContext = prevExecutionContext;
      
      
        // Check if the tree has completed.
        if (workInProgress !== null) {
          // 如果 workInProgress 不为 null,表示渲染未完成,返回 RootInProgress。
          return RootInProgress;
        } else {
          // Completed the tree.
      // 渲染完成,重置正在进行的根节点和渲染车道,完成并发更新队列 finishQueueingConcurrentUpdates,并返回根节点的退出状态 workInProgressRootExitStatus。
          workInProgressRoot = null;
          workInProgressRootRenderLanes = NoLanes;
      
          // It's safe to process the queue now that the render phase is complete.
          finishQueueingConcurrentUpdates();
      
          // Return the final exit status.
          return workInProgressRootExitStatus;
        }
      }
      outer: do {
      try {
        if (
          workInProgressSuspendedReason !== NotSuspended &&
          workInProgress !== null
        ) {
          // The work loop is suspended. We need to either unwind the stack or
          // replay the suspended component.
          const unitOfWork = workInProgress;
          const thrownValue = workInProgressThrownValue;
      
          // 处理不同的暂停原因
          resumeOrUnwind: switch (workInProgressSuspendedReason) {
      
          // 处理错误暂停,重置暂停原因和抛出的值,并调用 throwAndUnwindWorkLoop 函数。
            case SuspendedOnError: {
              // Unwind then continue with the normal work loop.
              workInProgressSuspendedReason = NotSuspended;
              workInProgressThrownValue = null;
              throwAndUnwindWorkLoop(
                root,
                unitOfWork,
                thrownValue,
                SuspendedOnError,
              );
              break;
            }
      
           // 处理数据错误
            case SuspendedOnData: {
              const thenable: Thenable<mixed> = (thrownValue: any);
              
              if (isThenableResolved(thenable)) {
                // 若已解析则重置暂停原因和抛出的值
                // The data resolved. Try rendering the component again.
                workInProgressSuspendedReason = NotSuspended;
                workInProgressThrownValue = null;
                replaySuspendedUnitOfWork(unitOfWork);
                break;
              }
      
              // 设置数据解析后的回调函数 onResolution
              const onResolution = () => {
                // Check if the root is still suspended on this promise.
                if (
                  workInProgressSuspendedReason === SuspendedOnData &&
                  workInProgressRoot === root
                ) {
                  // Mark the root as ready to continue rendering.
                  workInProgressSuspendedReason = SuspendedAndReadyToContinue;
                }
                
                ensureRootIsScheduled(root);
              };
              
              thenable.then(onResolution, onResolution);
              break outer;
            }
      
            // 设置暂停原因为已准备好继续。
            case SuspendedOnImmediate: {
              workInProgressSuspendedReason = SuspendedAndReadyToContinue;
              break outer;
            }
              
            // 设置暂停原因为已准备好继续。
            case SuspendedOnInstance: {
              workInProgressSuspendedReason =
                SuspendedOnInstanceAndReadyToContinue;
              break outer;
            }
      
            // 检查数据是否已解析
            case SuspendedAndReadyToContinue: {
              const thenable: Thenable<mixed> = (thrownValue: any);
              if (isThenableResolved(thenable)) {
                // The data resolved. Try rendering the component again.
                workInProgressSuspendedReason = NotSuspended;
                workInProgressThrownValue = null;
                replaySuspendedUnitOfWork(unitOfWork);
              } else {
                // Otherwise, unwind then continue with the normal work loop.
                workInProgressSuspendedReason = NotSuspended;
                workInProgressThrownValue = null;
                throwAndUnwindWorkLoop(
                  root,
                  unitOfWork,
                  thrownValue,
                  SuspendedAndReadyToContinue,
                );
              }
              break;
            }
              
            case SuspendedOnInstanceAndReadyToContinue: {
              let resource: null | Resource = null;
              // 根据 workInProgress.tag 的值处理不同类型的暂停,检查实例是否已准备好,若已准备好则更新工作进度
              switch (workInProgress.tag) {
                case HostHoistable: {
                  resource = workInProgress.memoizedState;
                }
                // intentional fallthrough
                case HostComponent:
                case HostSingleton: {
           
                  const hostFiber = workInProgress;
                  const type = hostFiber.type;
                  const props = hostFiber.pendingProps;
                  const isReady = resource
                    ? preloadResource(resource)
                    : preloadInstance(type, props);
                  
                  if (isReady) {
                    
                    workInProgressSuspendedReason = NotSuspended;
                    workInProgressThrownValue = null;
                    const sibling = hostFiber.sibling;
                    if (sibling !== null) {
                      workInProgress = sibling;
                    } else {
                      
                      const returnFiber = hostFiber.return;
                      if (returnFiber !== null) {
                        workInProgress = returnFiber;
                        completeUnitOfWork(returnFiber);
                      } else {
                        workInProgress = null;
                      }
                    }
                    break resumeOrUnwind;
                  }
                  break;
                }
                default: {
                  break;
                }
              }
              // Otherwise, unwind then continue with the normal work loop.
              workInProgressSuspendedReason = NotSuspended;
              workInProgressThrownValue = null;
              throwAndUnwindWorkLoop(
                root,
                unitOfWork,
                thrownValue,
                SuspendedOnInstanceAndReadyToContinue,
              );
              break;
            }
              
            case SuspendedOnDeprecatedThrowPromise: {
              workInProgressSuspendedReason = NotSuspended;
              workInProgressThrownValue = null;
              throwAndUnwindWorkLoop(
                root,
                unitOfWork,
                thrownValue,
                SuspendedOnDeprecatedThrowPromise,
              );
              break;
            }
              
            case SuspendedOnHydration: {
              resetWorkInProgressStack();
              workInProgressRootExitStatus = RootDidNotComplete;
              break outer;
            }
              
            default: {
              throw new Error(
                'Unexpected SuspendedReason. This is a bug in React.',
              );
            }
          }
        }
      
        // 执行并发工作循环
        workLoopConcurrent();
        
        break;
      } catch (thrownValue) {
        handleThrow(root, thrownValue);
      }
      } while (true);

       

      renderRootSync

      renderRootSync 函数是 React 渲染流程中用于同步渲染根节点的核心函数。它会设置执行上下文,根据根节点和渲染优先级车道的变化重置渲染栈,然后开始同步渲染工作。在渲染过程中,会处理可能出现的暂停情况,最后恢复执行上下文并返回渲染的退出状态。

      参数说明

      • root: FiberRoot:表示 React 应用的根节点,是整个 Fiber 树的根,包含了应用的各种状态和信息。
      • lanes: Lanes:渲染优先级车道,用于标识当前渲染任务的优先级,不同的优先级车道决定了渲染任务的执行顺序。
      • shouldYieldForPrerendering: boolean:一个布尔值,指示是否应该为预渲染而让出控制权,在当前代码中未被使用,但可能在后续逻辑中有作用。
      function renderRootSync(
        root: FiberRoot,
        lanes: Lanes,
        shouldYieldForPrerendering: boolean,
      ): RootExitStatus {
        // 当前的执行上下文
        const prevExecutionContext = executionContext;
        // 将执行上下文设置为渲染上下文,表示当前处于渲染阶段。
        executionContext |= RenderContext;
        
        // 当前的调度器
        const prevDispatcher = pushDispatcher(root.containerInfo);
        // 异步调度器
        const prevAsyncDispatcher = pushAsyncDispatcher();
      
      // workInProgressRootRenderLanes代表当前正在处理的渲染优先级车道
        if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
      
          // 获取当前渲染优先级车道对应的过渡信息。
          workInProgressTransitions = getTransitionsForLanes(root, lanes);
          // 准备一个新的渲染栈,重置渲染状态。
          prepareFreshStack(root, lanes);
        }
      
        // 用于标记在渲染过程中是否在 shell 中发生了暂停。
        let didSuspendInShell = false;
        // 存储渲染的退出状态
        let exitStatus = workInProgressRootExitStatus;
      
        // 省略代码do..while
      
        // 如果在渲染过程中发生了暂停(didSuspendInShell 为 true),则增加根节点的 shellSuspendCounter 计数器。
          if (didSuspendInShell) {
          root.shellSuspendCounter++;
        }
      
        // 重置上下文依赖,确保上下文状态的一致性。
        resetContextDependencies();
      
        // 恢复之前保存的执行上下文。
        executionContext = prevExecutionContext;
        // 恢复之前保存的调度器。
        popDispatcher(prevDispatcher);
        // 恢复之前保存的异步调度器。
        popAsyncDispatcher(prevAsyncDispatcher);
      
      
        if (workInProgress !== null) {
          // Did not complete the tree. This can happen if something suspended in
          // the shell.
        } else {
          // Normal case. We completed the whole tree.
      
          // Set this to null to indicate there's no in-progress render.
          workInProgressRoot = null;
          // 表示没有正在进行的渲染任务
          workInProgressRootRenderLanes = NoLanes;
      
       // 调用 finishQueueingConcurrentUpdates() 函数处理并发更新队列。
          finishQueueingConcurrentUpdates();
        }
      
        return exitStatus;
      }

       

      workLoopConcurrent

      workLoopConcurrent 函数是 React 并发模式下执行工作循环的核心函数。它的主要职责是在存在未完成的工作(workInProgress)且不需要让步(shouldYield 返回 false)的情况下,持续执行单元工作(performUnitOfWork),从而推进渲染流程,处理 Fiber 树中的任务。

      function workLoopConcurrent() {
        // Perform work until Scheduler asks us to yield
        while (workInProgress !== null && !shouldYield()) {
          // $FlowFixMe[incompatible-call] found when upgrading Flow
          performUnitOfWork(workInProgress);
        }
      }

      workLoopSync 

      workLoopSync 函数是 React 渲染流程中的一个关键函数,它实现了同步工作循环。在这个循环里,会持续处理 workInProgress 指向的 Fiber 节点,直到 workInProgress 变为 null,也就是没有更多的 Fiber 节点需要处理为止。

      function workLoopSync() {
        // Perform work without checking if we need to yield between fiber.
        while (workInProgress !== null) {
          performUnitOfWork(workInProgress);
        }
      }

       

       finishConcurrentRender

      finishConcurrentRender 函数是 React 并发渲染流程中用于处理渲染完成状态的核心函数。它根据渲染的退出状态(exitStatus)执行不同的操作,包括处理渲染进行中、致命错误、延迟暂停、普通错误、已完成等状态。函数还会更新根节点(root)的相关信息,如已完成的工作(finishedWork)和已完成的车道(finishedLanes),并根据条件决定是否进行提交操作(commitRootWhenReady)或标记根节点为暂停状态(markRootSuspended

      函数参数含义

      • root:类型为 FiberRoot,代表 React 应用的根节点,包含了整个应用的渲染信息。
      • exitStatus:类型为 RootExitStatus,表示渲染的退出状态,用于判断渲染过程是完成、出错、暂停等情况。
      • finishedWork:类型为 Fiber,是完成渲染的 Fiber 节点树。
      • lanes:类型为 Lanes,表示渲染的优先级车道,用于区分不同类型和优先级的渲染任务。
        function finishConcurrentRender(
          root: FiberRoot,
          exitStatus: RootExitStatus,
          finishedWork: Fiber,
          lanes: Lanes,
        ) {
          // 初始化渲染结束时间
          let renderEndTime = 0;
        
          // 根据退出状态执行不同的操作
          switch (exitStatus) {
            // 渲染仍在进行中或者出现致命错误
            case RootInProgress:
            case RootFatalErrored: {
              throw new Error('Root did not complete. This is a bug in React.');
            }
            case RootSuspendedWithDelay: {
              // 如果渲染是延迟暂停的,并且 lanes 中只包含过渡任务(通过 includesOnlyTransitions 函数判断),则标记根节点为暂停状态(调用 markRootSuspended 函数),并直接返回。如果不满足该条件,则继续执行后续代码。
              if (includesOnlyTransitions(lanes)) {
                const didAttemptEntireTree =
                  !workInProgressRootDidSkipSuspendedSiblings;
                markRootSuspended(
                  root,
                  lanes,
                  workInProgressDeferredLane,
                  didAttemptEntireTree,
                );
                return;
              }
              // Commit the placeholder.
              break;
            }
            case RootErrored: {
              workInProgressRootRecoverableErrors = null;
              break;
            }
            case RootSuspended:
            case RootCompleted: {
              break;
            }
            default: {
              throw new Error('Unknown root exit status.');
            }
          }
        
          // 更新节点信息
          root.finishedWork = finishedWork;
          root.finishedLanes = lanes;
        
          // 若包含重试车道 
          if (
            includesOnlyRetries(lanes) &&
            (alwaysThrottleRetries || exitStatus === RootSuspended)
          ) {
            // 计算距离超时时间
            const msUntilTimeout =
              globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();
            
            if (msUntilTimeout > 10) {
              const didAttemptEntireTree =
                !workInProgressRootDidSkipSuspendedSiblings;
              
              // 标记根节点为暂停状态
              markRootSuspended(
                root,
                lanes,
                workInProgressDeferredLane,
                didAttemptEntireTree,
              );
        
              // 获取下一个车道(nextLanes)
              const nextLanes = getNextLanes(root, NoLanes);
              if (nextLanes !== NoLanes) {
                return;
              }
        
              // 用 scheduleTimeout 函数在 msUntilTimeout 毫秒后执行 commitRootWhenReady 函数进行提交操作,并返回。
              root.timeoutHandle = scheduleTimeout(
                commitRootWhenReady.bind(
                  null,
                  root,
                  finishedWork,
                  workInProgressRootRecoverableErrors,
                  workInProgressTransitions,
                  workInProgressRootDidIncludeRecursiveRenderUpdate,
                  lanes,
                  workInProgressDeferredLane,
                  workInProgressRootInterleavedUpdatedLanes,
                  workInProgressSuspendedRetryLanes,
                  workInProgressRootDidSkipSuspendedSiblings,
                  THROTTLED_COMMIT,
                  renderStartTime,
                  renderEndTime,
                ),
                msUntilTimeout,
              );
              return;
            }
          }
        
          // 执行提交操作
          commitRootWhenReady(
            root,
            finishedWork,
            workInProgressRootRecoverableErrors,
            workInProgressTransitions,
            workInProgressRootDidIncludeRecursiveRenderUpdate,
            lanes,
            workInProgressDeferredLane,
            workInProgressRootInterleavedUpdatedLanes,
            workInProgressSuspendedRetryLanes,
            workInProgressRootDidSkipSuspendedSiblings,
            IMMEDIATE_COMMIT,
            renderStartTime,
            renderEndTime,
          );
          
        }

         

        commitRootWhenReady

        commitRootWhenReady 函数的主要功能是在满足一定条件时提交根节点的渲染结果。它会检查 Fiber 树的子树标志,判断是否需要暂停提交。如果需要暂停,会启动暂停提交流程,累积与 Suspense 相关的提交信息,并等待渲染器准备好提交。如果不需要暂停,则直接调用 commitRoot 函数进行提交。

        函数参数含义

        • root,根节点 fiber root。
        • finishedWork,已完成的 root fiber。
        • recoverableErrors,可恢复错误。
        • transitions,过渡信息。
        • didIncludeRenderPhaseUpdate,是否包含渲染阶段更新。
        • lanes,车道。
        • updatedLanes,更新的车道。
        • suspendedRetryLanes,暂停重试车道。
        • suspendedCommitReason,暂停提交原因。
        • completedRenderStartTime,完成渲染的开始时间。
        • completedRenderEndTime,完成渲染的结束时间。
        function commitRootWhenReady(
          root: FiberRoot,
          finishedWork: Fiber,
          recoverableErrors: Array<CapturedValue<mixed>> | null,
          transitions: Array<Transition> | null,
          didIncludeRenderPhaseUpdate: boolean,
          lanes: Lanes,
          spawnedLane: Lane,
          updatedLanes: Lanes,
          suspendedRetryLanes: Lanes,
          didSkipSuspendedSiblings: boolean,
          suspendedCommitReason: SuspendedCommitReason, // Profiling-only
          completedRenderStartTime: number, // Profiling-only
          completedRenderEndTime: number, // Profiling-only
        ) {
        
          const BothVisibilityAndMaySuspendCommit = Visibility | MaySuspendCommit;
        // 获取 finishedWork(已完成的 Fiber 节点)的子树标志,用于判断子树是否需要暂停提交。
          const subtreeFlags = finishedWork.subtreeFlags;
        
        // 判断是否需要暂停提交
          if (
            subtreeFlags & ShouldSuspendCommit ||
            (subtreeFlags & BothVisibilityAndMaySuspendCommit) ===
              BothVisibilityAndMaySuspendCommit
          ) {
            // 启动暂停提交的流程,设置相关状态。
            startSuspendingCommit();
        
            // 收集需要暂停提交的 Fiber 节点信息,可能是包含 Suspense 组件的部分。
            accumulateSuspenseyCommit(finishedWork);
        
            // 返回一个函数,用于在条件满足时调度提交操作。如果返回 null,表示无法暂停,需立即提交。
            const schedulePendingCommit = waitForCommitToBeReady();
            
            if (schedulePendingCommit !== null) {
              // 调度延迟提交
              root.cancelPendingCommit = schedulePendingCommit(
                commitRoot.bind(
                  null,
                  root,
                  recoverableErrors,
                  transitions,
                  didIncludeRenderPhaseUpdate,
                  spawnedLane,
                  updatedLanes,
                  suspendedRetryLanes,
                  SUSPENDED_COMMIT,
                  completedRenderStartTime,
                  completedRenderEndTime,
                ),
              );
              const didAttemptEntireTree = !didSkipSuspendedSiblings;
              markRootSuspended(root, lanes, spawnedLane, didAttemptEntireTree);
              return;
            }
          }
        
          // Otherwise, commit immediately.;
        // 如果不需要暂停提交,直接调用 commitRoot 函数进行根节点的提交操作。
          commitRoot(
            root,
            recoverableErrors,
            transitions,
            didIncludeRenderPhaseUpdate,
            spawnedLane,
            updatedLanes,
            suspendedRetryLanes,
            suspendedCommitReason,
            completedRenderStartTime,
            completedRenderEndTime,
          );
        }

        工具函数之 pickArbitraryLaneIndex

        function pickArbitraryLaneIndex(lanes: Lanes) {
          return 31 - clz32(lanes);
        }
        const clz32: (x: number) => number = Math.clz32
          ? Math.clz32
          : clz32Fallback;
        
        const log = Math.log;
        const LN2 = Math.LN2;
        
        function clz32Fallback(x: number): number {
          const asUint = x >>> 0;
          if (asUint === 0) {
            return 32;
          }
          return (31 - ((log(asUint) / LN2) | 0)) | 0;
        }
        

        Math.clz32() 函数返回一个数字在转换成 32 无符号整形数字的二进制形式后,开头的 0 的个数,比如 1000000 转换成 32 位无符号整形数字的二进制形式后是 00000000000011110100001001000000, 开头的 0 的个数是 12 个,则 Math.clz32(1000000) 返回 12

         工具函数之 computeExpirationTime

        computeExpirationTime 函数的主要作用是根据传入的车道(Lane)类型和当前时间(currentTime),计算并返回对应的过期时间。不同类型的车道有不同的过期时间计算逻辑,该函数通过 switch 语句来处理各种车道类型。

        function computeExpirationTime(lane: Lane, currentTime: number) {
          switch (lane) {
              // 处理同步相关车道
            case SyncHydrationLane:
            case SyncLane:
            case InputContinuousHydrationLane:
            case InputContinuousLane:
              // syncLaneExpirationMs 同步车道的过期时间间隔
              return currentTime + syncLaneExpirationMs;
        
            // 处理过渡相关车道
            case DefaultHydrationLane:
            case DefaultLane:
            case TransitionHydrationLane:
            case TransitionLane1:
            case TransitionLane2:
            case TransitionLane3:
            case TransitionLane4:
            case TransitionLane5:
            case TransitionLane6:
            case TransitionLane7:
            case TransitionLane8:
            case TransitionLane9:
            case TransitionLane10:
            case TransitionLane11:
            case TransitionLane12:
            case TransitionLane13:
            case TransitionLane14:
            case TransitionLane15:
               // transitionLaneExpirationMs(过渡车道的过期时间间隔
              return currentTime + transitionLaneExpirationMs;
        
            // 处理重试车道
            case RetryLane1:
            case RetryLane2:
            case RetryLane3:
            case RetryLane4:
              return enableRetryLaneExpiration
                ? currentTime + retryLaneExpirationMs
                 // NoTimestamp,表示没有设置过期时间。
                : NoTimestamp;
              
            case SelectiveHydrationLane:
            case IdleHydrationLane:
            case IdleLane:
            case OffscreenLane:
            case DeferredLane:
              return NoTimestamp;
              
            default:
              return NoTimestamp;
          }
        }

        工具函数之 markRootSuspended

        markRootSuspended 函数的主要功能是将 FiberRoot 标记为挂起状态。在 React 的协调过程中,当渲染因为某些原因(如数据未就绪)被挂起时,会调用此函数来更新根节点的状态信息,包括挂起车道、已处理车道、过期时间等,同时处理可能产生的延迟车道。

        函数参数含义:

        • root:类型为 FiberRoot,代表 React 应用的根节点。
        • suspendedLanes:类型为 Lanes,表示当前被挂起的车道集合。
        • spawnedLane:类型为 Lane,表示在挂起过程中产生的延迟车道。
        • didAttemptEntireTree:类型为 boolean,表示是否尝试渲染整个 Fiber 树。
        function markRootSuspended(
          root: FiberRoot,
          suspendedLanes: Lanes,
          spawnedLane: Lane,
          didAttemptEntireTree: boolean,
        ) {
          // 标记这些车道为挂起状态。
          root.suspendedLanes |= suspendedLanes;
          // 表示这些车道不再是已处理状态。
          root.pingedLanes &= ~suspendedLanes;
        
          // 如果启用了兄弟节点预渲染(enableSiblingPrerendering 为 true)且尝试渲染了整个 Fiber 树(didAttemptEntireTree 为 true)
          if (enableSiblingPrerendering && didAttemptEntireTree) {
           // 标记这些车道为已预热状态。
            root.warmLanes |= suspendedLanes;
          } 
        
          // 从 root 中获取 expirationTimes 数组,该数组存储了每个车道的过期时间。
          const expirationTimes = root.expirationTimes;
          
          let lanes = suspendedLanes;
          
          // 使用 while 循环遍历 suspendedLanes 中的每个车道
          while (lanes > 0) {
            // 调用 pickArbitraryLaneIndex 函数获取一个车道的索引。
            const index = pickArbitraryLaneIndex(lanes);
            
            // 使用左移运算符 << 生成对应的车道掩码。
            const lane = 1 << index;
        
            // 将该车道在 expirationTimes 数组中的过期时间设置为 NoTimestamp,表示该车道不再有过期时间。
            expirationTimes[index] = NoTimestamp;
        
            // 使用按位与运算符 &= 和按位取反运算符 ~ 从 lanes 中移除已处理的车道。
            lanes &= ~lane;
          }
        
          // 如果 spawnedLane 不是 NoLane,则调用 markSpawnedDeferredLane 函数处理在挂起过程中产生的延迟车道。
          if (spawnedLane !== NoLane) {
            markSpawnedDeferredLane(root, spawnedLane, suspendedLanes);
          }
        }

        这里介绍了root.render的过程,后续将继续深入阅读performUnitOfWork函数和commitRoot函数。

        <think>好的,用户想调试React 19源码。首先,我需要回想一下React源码调试的一般步骤,并结合React 19的可能变化。根据引用内容中的信息,React 19源码结构可能与之前的版本类似,但需要确认是否有新的模块或构建工具的变化。 用户可能需要先获取源码,所以第一步应该是克隆React的官方仓库。然后切换到正确的分支或标签,比如v19.0.0或者其他相关版本。接下来,安装依赖,通常使用yarn。构建源码可能需要执行构建命令,比如yarn build,但需要注意构建参数,比如生成调试模式的构建产物,可能需要加上--type debug之类的参数。 调试的话,用户可能想用Chrome DevTools,所以需要生成sourcemap。在构建时确保启用sourcemap,这样在浏览器中调试时可以映射到原始文件。另外,可能需要配置本地示例项目,通过yarn link将本地构建的React链接到示例项目中,这样修改源码后可以实时测试。 还要考虑React的Fiber架构和双缓存机制(引用3中提到),这可能影响调试时的组件更新流程。使用useRef或Hooks相关的代码(引用2中的例子)时,调试可能需要跟踪ref的创建和更新过程。 用户可能遇到构建错误或依赖问题,需要提醒常见问题,比如Node.js版本是否合适,是否安装了正确的构建工具链。此外,建议使用VSCode的调试配置或者Chrome的调试工具,设置断点,特别是React的核心模块,如ReactDOM、调度器等。 最后,生成相关问题时要围绕源码结构、构建问题、调试技巧、常见错误等,帮助用户深入理解。</think>### 如何调试 React 19 源码? #### 1. 获取 React 19 源码 首先克隆 React 仓库并切换到目标分支: ```bash git clone https://github.com/facebook/react.git cd react git checkout v19.0.0 # 假设已发布正式版本,否则使用 main 分支 ``` #### 2. 安装依赖并构建调试版本 React 使用 `yarn` 管理依赖,构建需启用 `sourcemap`: ```bash yarn install yarn build --type=debug # 生成带调试信息的构建产物 ``` 这会生成 `build` 目录,包含 `react.development.js` 和 `react-dom.development.js` 等文件,同时生成 `sourcemap` 文件以支持源码映射[^3]。 #### 3. 配置本地调试环境 通过 `yarn link` 将本地构建的 React 链接到示例项目: ```bash cd build/node_modules/react yarn link cd ../react-dom yarn link # 在示例项目中链接本地 React cd your-test-project yarn link react react-dom ``` #### 4. 使用 Chrome DevTools 调试 1. **启用 Sourcemap**:在 Chrome 开发者工具的 `Settings > Sources` 中确保 `Enable JavaScript source maps` 已勾选。 2. **断点设置**:在 `Sources` 面板中找到 `react.development.js` 或 `react-dom.development.js`,通过 `Ctrl+P` 搜索源码文件(如 `ReactFiberWorkLoop.js`),直接添加断点。 3. **跟踪 Hooks 流程**:例如调试 `useRef` 时,可定位到 `ReactHooks.js` 中的 `useRef` 定义,观察其如何通过 `currentDispatcher` 调用具体实现[^2]。 #### 5. 使用 VSCode 调试 配置 `.vscode/launch.json`: ```json { "version": "0.2.0", "configurations": [ { "type": "chrome", "request": "launch", "name": "Debug React", "url": "http://localhost:3000", // 示例项目本地地址 "webRoot": "${workspaceFolder}/your-test-project/src", "sourceMaps": true } ] } ``` #### 6. 关键源码模块定位 - **Fiber 架构**:关注 `packages/react-reconciler/src/ReactFiber*` 文件,涉及组件树更新和双缓存机制[^3]。 - **Hooks 实现**:查看 `packages/react/src/ReactHooks.js` 和 `packages/react-reconciler/src/ReactFiberHooks.js`,追踪 `useRef` 的创建与更新逻辑[^2]。 - **渲染流程**:`react-dom` 的 `src/client/ReactDOMRoot.js` 包含 `render` 方法入口。 #### 常见问题解决 - **构建失败**:确保 Node.js 版本 ≥ 16,检查 `yarn` 版本兼容性。 - **断点不生效**:确认构建时启用了 `--type=debug`,并清空浏览器缓存。 - **Hooks 状态追踪**:使用 React DevTools 的 “Components” 面板查看组件内部状态。 ---
        评论
        添加红包

        请填写红包祝福语或标题

        红包个数最小为10个

        红包金额最低5元

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

        抵扣说明:

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

        余额充值