接着上文,当我们确定了任务优先级后,调度并发任务scheduleCallback
scheduleCallback有两个入参,一个是schedulerPriorityLevel
,一个是回调函数performConcurrentWorkOnRoot
1.performConcurrentWorkOnRoot
scheduleCallback的回调函数,并发模式的核心调度函数
负责在浏览器空闲时间中异步地执行根节点的更新工作
该函数会不断检查是否有空闲时间,如果有,则执行一个工作单元(work unit),否则会暂停执行,直到有空闲时间再继续
错误处理流程图
graph TD
A[检测exitStatus] --> B{是否RootErrored?}
B -->|是| C[获取错误重试车道]
C --> D{有效车道?}
D -->|是| E[同步重试渲染]
B -->|否| F{是否RootFatalErrored?}
F -->|是| G[准备新堆栈并抛出错误]
B -->|否| H{是否RootDidNotComplete?}
H -->|是| I[标记根为暂停状态]
H -->|否| J[完成渲染提交准备]
// 该函数是并发渲染的入口点,负责协调渲染阶段的打断与恢复
// didTimeout 参数表示当前任务是否因时间分片用完而超时
function performConcurrentWorkOnRoot(root, didTimeout) {
// 判断是否需要中断渲染(通过 Scheduler 的 shouldYield 机制)
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
throw new Error('Should not already be working.');
}
// 执行所有被调度的被动效果(useEffect回调)
// 包含清理函数和执行新effect的逻辑
// 返回布尔值表示是否有effects被执行
flushPassiveEffects()
// 获取当前渲染优先级车道
let lanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
);
// 判断是否需要进行时间切片的条件
const shouldTimeSlice =
// 检查当前车道是否不包含阻塞性车道(需要同步执行的紧急任务)
!includesBlockingLane(root, lanes) &&
// 调度器超时配置检查(调试选项)或当前未超时
!includesExpiredLane(root, lanes) &&
(disableSchedulerTimeoutInWorkLoop || !didTimeout);
// 根据条件选择渲染模式
let exitStatus = shouldTimeSlice
? renderRootConcurrent(root, lanes) // 可中断的并发模式
: renderRootSync(root, lanes); // 同步阻塞模式
//React并发渲染错误恢复机制
if (exitStatus === RootErrored) {
//当发生可恢复错误时,通过 getLanesToRetrySynchronouslyOnError 获取需要同步重试的车道(lanes)
const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
if (errorRetryLanes !== NoLanes) {
lanes = errorRetryLanes;
//调用 recoverFromConcurrentError 进行错误恢复,切换为同步渲染模式
exitStatus = recoverFromConcurrentError(root, errorRetryLanes);
}
}
//致命错误处理
if (exitStatus === RootFatalErrored) {
const fatalError = workInProgressRootFatalError;
prepareFreshStack(root, NoLanes); // 重置Fiber树
markRootSuspended(root, lanes); // 标记根节点为暂停状态
ensureRootIsScheduled(root, now()); // 重新调度
throw fatalError; // 抛出错误给上层边界
}
//外部状态一致性检查
if (renderWasConcurrent &&
!isRenderConsistentWithExternalStores(finishedWork)) {
exitStatus = renderRootSync(root, lanes); // 强制同步渲染
}
//渲染完成处理
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
//调用 finishConcurrentRender 提交渲染结果
finishConcurrentRender(root, exitStatus, lanes);
}
1.flushPassiveEffects处理被动效果
flushPassiveEffects
是React 中处理被动效果(Passive Effects)的核心函数 ,主要功能是执行 useEffect 相关的副作用清理和触发操作
commitRoot(提交阶段)
→ scheduleCallback(调度被动效果)
→ flushPassiveEffects(执行副作用)
→ flushPassiveEffectsImpl(实际处理)
→ commitPassiveUnmountEffects(清理旧 effect)
→ commitPassiveMountEffects(触发新 effect)
export function flushPassiveEffects(): boolean {
if (rootWithPendingPassiveEffects !== null) {
// 缓存待处理 passive effects 的根节点和相关 lanes
const root = rootWithPendingPassiveEffects;
const remainingLanes = pendingPassiveEffectsRemainingLanes;
pendingPassiveEffectsRemainingLanes = NoLanes;
// 计算优先级
const renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);
const priority = lowerEventPriority(DefaultEventPriority, renderPriority);
// 保存当前上下文
const prevTransition = ReactCurrentBatchConfig.transition;
const previousPriority = getCurrentUpdatePriority();
try {
// 设置新优先级并执行副作用
ReactCurrentBatchConfig.transition = null;
setCurrentUpdatePriority(priority);
return flushPassiveEffectsImpl(); // 实际执行入口
} finally {
// 恢复上下文
setCurrentUpdatePriority(previousPriority);
ReactCurrentBatchConfig.transition = prevTransition;
// 释放缓存池
releaseRootPooledCache(root, remainingLanes);
}
}
return false;
}
实际执行的是flushPassiveEffectsImpl
函数
2.flushPassiveEffectsImpl 处理被动效果实现函数
这个函数是React处理被动副作用(如 useEffect )的核心机制,在浏览器空闲时段批量处理:
- 执行已卸载组件的清理函数
- 执行新effect的回调函数
- 收集性能分析数据
- 处理过渡追踪逻辑
function flushPassiveEffectsImpl() {
//检查是否存在待处理的被动效果树 rootWithPendingPassiveEffects ,若不存在直接返回false
if (rootWithPendingPassiveEffects === null) {
return false;
}
// 获取当前需要处理的Fiber根节点和对应的优先级车道(lanes)
// 重置相关状态变量
const root = rootWithPendingPassiveEffects;
const lanes = pendingPassiveEffectsLanes;
rootWithPendingPassiveEffects = null;
pendingPassiveEffectsLanes = NoLanes;
//分为两个阶段:先执行所有卸载的清理函数,再执行新的effect回调
commitPassiveUnmountEffects(root.current); // 执行清理函数(useEffect返回的函数)
commitPassiveMountEffects(root, root.current, lanes, transitions); // 执行effect回调
//调度处理过渡效果的callback,使用空闲优先级
if (enableTransitionTracing) {
scheduleCallback(IdleSchedulerPriority, () =>
processTransitionCallbacks(...)
)
}
}
commitPassiveUnmountEffects 被动卸载函数
实际调用的是commitPassiveUnmountEffects_begin
函数
这是React被动卸载效果处理的入口函数,主要负责处理被删除Fiber树的useEffect清理函数。属于commit
阶段passive effects处理流程的一部分
- 遍历 nextEffect 链表处理被标记为ChildDeletion的fiber
- 调用 commitPassiveUnmountEffectsInsideOfDeletedTree_begin 处理被删除树内部的effect清理
- 根据 deletedTreeCleanUpLevel 配置执行不同级别的内存清理
- 通过深度优先遍历处理所有被标记PassiveMask的子树
function commitPassiveUnmountEffects_begin() {
while (nextEffect !== null) {
const fiber = nextEffect;
// 处理子节点删除的清理
if ((fiber.flags & ChildDeletion) !== NoFlags) {
// 遍历所有待删除的fiber
deletions.forEach(fiberToDelete => {
commitPassiveUnmountEffectsInsideOfDeletedTree_begin(fiberToDelete);
});
// 执行深度清理(断开alternate链接)
if (deletedTreeCleanUpLevel >= 1) {
// 断开被删除fiber与之前兄弟节点的链接
previousFiber.child = null;
while(detachedChild) {
detachedChild.sibling = null;
detachedChild = detachedChild.sibling;
}
}
}
// 递归处理子树
if ((fiber.subtreeFlags & PassiveMask) && fiber.child) {
nextEffect = fiber.child;
} else {
commitPassiveUnmountEffects_complete();
}
}
}
commitPassiveMountEffects
实际调用commitPassiveMountEffects_begin
函数
该函数是React提交阶段处理被动挂载效果(Passive Effects)的入口函数,主要职责是遍历Fiber树并触发useEffect等副作用
- 遍历逻辑 通过 while (nextEffect !== null) 循环遍历Fiber链表,使用 nextEffect 指针进行深度优先遍历
- 子树检测 通过 (fiber.subtreeFlags & PassiveMask) !== NoFlags 检查当前Fiber子树是否包含需要处理的被动效果
- 遍历方向控制
- 当子树存在被动效果时,将 nextEffect 指向第一个子节点继续深入遍历
- 当子树没有被动效果时,调用 commitPassiveMountEffects_complete 切换到"complete"阶段处理兄弟节点或向上回溯
function commitPassiveMountEffects_begin(
subtreeRoot: Fiber,
root: FiberRoot,
committedLanes: Lanes,
committedTransitions: Array<Transition> | null,
) {
while (nextEffect !== null) {
const fiber = nextEffect;
const firstChild = fiber.child;
//检查当前Fiber子树是否包含需要处理的被动效果
if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && firstChild !== null) {
firstChild.return = fiber;
nextEffect = firstChild;
} else {
commitPassiveMountEffects_complete(
subtreeRoot,
root,
committedLanes,
committedTransitions,
);
}
}
}
3.renderRootConcurrent 并发渲染函数
renderRootConcurrent
负责协调整个Fiber树的异步渲染流程,实现了可中断的渲染机制
function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
// 保存执行上下文
const prevExecutionContext = executionContext;
executionContext |= RenderContext;
// 初始化更新器跟踪
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
if (enableUpdaterTracking) {
// 开发工具相关的更新器状态维护
restorePendingUpdaters(root, workInProgressRootRenderLanes);
movePendingFibersToMemoized(root, lanes);
}
// 初始化过渡状态和渲染计时器
workInProgressTransitions = getTransitionsForLanes(root, lanes);
resetRenderTimer();
prepareFreshStack(root, lanes); // 创建新的工作循环堆栈
}
// 开发模式调试跟踪
if (__DEV__ && enableDebugTracing) {
logRenderStarted(lanes);
}
// 启动并发工作循环
do {
try {
workLoopConcurrent(); // 核心工作循环
break;
} catch (thrownValue) {
handleError(root, thrownValue); // 错误边界处理
}
} while (true);
// 清理阶段
resetContextDependencies();
popDispatcher(prevDispatcher);
executionContext = prevExecutionContext;
// 返回渲染结果状态
return workInProgress !== null ? RootInProgress : workInProgressRootExitStatus;
}
并发渲染控制函数 workLoopConcurrent
该函数实现了并发模式下,React使用可中断的渲染机制,允许在浏览器空闲时执行任务,避免阻塞主线程
workInProgress是全局变量,表示当前正在处理的Fiber节点。
shouldYield()函数来自调度器(Scheduler),用于判断是否需要让出主线程,以便处理更高优先级的任务或用户交互。如果返回true,循环终止,渲染任务会被暂停,等待下次空闲时间继续。
function workLoopConcurrent() {
// 执行工作直到调度器要求让出主线程
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
让步检测函数 shouldYield 时间分片控制
该函数通常用于时间分片,确保长时间的任务不会阻塞主线程
时间分片控制
- frameInterval (默认 5ms) 单帧时间阈值
- continuousInputInterval (默认 50ms) 连续输入检测阈值
- maxInterval (默认 300ms) 最大阻塞时间
采用三级检测机制:
- 优先检测 needsPaint 标记的绘制请求
- 短期阻塞仅检测离散输入(点击/按键)
- 长期阻塞检测所有输入类型
- 超长阻塞(>300ms)强制让出主线程
function shouldYieldToHost() {
// 计算主线程已阻塞的时间
const timeElapsed = getCurrentTime() - startTime;
// 短期阻塞无需让出(小于单帧时间)
if (timeElapsed < frameInterval) {
return false;
}
// 长期阻塞处理逻辑
if (enableIsInputPending) {
// 优先处理绘制请求
if (needsPaint) {
return true;
}
// 分级检测输入事件
if (timeElapsed < continuousInputInterval) {
return isInputPending?.(); // 仅检测离散输入
} else if (timeElapsed < maxInterval) {
return isInputPending?.(continuousOptions); // 检测所有输入类型
} else {
return true; // 超长阻塞强制让出
}
}
// 后备策略:无输入检测能力时默认让出
return true;
}
performUnitOfWork
该函数主要负责处理单个fiber
- Fiber节点处理入口
- 接收 unitOfWork 参数作为当前要处理的Fiber节点
- 通过 alternate 获取对应的current fiber(已提交的fiber树节点)
- 调试与性能分析
- setCurrentDebugFiberInDEV 设置开发环境下的调试标记
- 当启用性能分析时( enableProfilerTimer ),用 start/stopProfilerTimer 包裹beginWork执行过程
graph TD
A[开始处理] --> B{是否启用性能分析?}
B -->|是| C[开始计时]
B -->|否| D[执行beginWork]
C --> D
D --> E[停止计时]
E --> F[处理返回的next节点]
D --> F
F --> G{next存在?}
G -->|是| H[设置workInProgress指针]
G -->|否| I[完成当前节点]
function performUnitOfWork(unitOfWork: Fiber): void {
const current = unitOfWork.alternate;
setCurrentDebugFiberInDEV(unitOfWork);
let next;
//根据是否启用性能分析计时器(`enableProfilerTimer`)和当前Fiber的模式(`ProfileMode`),决定是否在`beginWork`前后进行性能测量
if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
startProfilerTimer(unitOfWork);
next = beginWork(current, unitOfWork, subtreeRenderLanes);
stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
} else {
next = beginWork(current, unitOfWork, subtreeRenderLanes);
}
resetCurrentDebugFiberInDEV();
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
//当前节点没有子节点完成当前节点的工作
completeUnitOfWork(unitOfWork);
} else {
//继续处理下一个节点
workInProgress = next;
}
ReactCurrentOwner.current = null;
}
beginWork
beginWork
函数是React协调过程的一部分,负责处理Fiber节点的更新。
它接收当前Fiber节点(current)、工作中的Fiber节点(workInProgress)以及渲染优先级(renderLanes)作为参数,返回下一个要处理的子Fiber节点或null
graph TD
A[beginWork] --> B{current存在?}
B -->|是| C[比较props/context]
B -->|否| D[处理hydration]
C --> E[设置didReceiveUpdate]
E --> F[根据tag分发]
D --> F
F --> G[FunctionComponent]
F --> H[ClassComponent]
F --> I[HostRoot等20+类型]
渲染循环 → performUnitOfWork → beginWork → 组件特定更新方法
↓
生成子fiber树并返回下一个工作单元
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
// ... 开发环境处理 ...
// 核心逻辑
if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
// 比较新旧props/context
// 热更新
if (oldProps !== newProps || hasLegacyContextChanged()) {
didReceiveUpdate = true;
} else {
// 检查调度更新或context变化
const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
current,
renderLanes,
)
//无更新且非错误边界重试
if (
!hasScheduledUpdateOrContext &&
(workInProgress.flags & DidCapture) === NoFlags
) {
didReceiveUpdate = false;
//提前退出优化
return attemptEarlyBailoutIfNoScheduledUpdate(
current,
workInProgress,
renderLanes,
);
}
}
} else {
// 处理hydration场景的ID生成
}
// 清除当前fiber的优先级标记
workInProgress.lanes = NoLanes;
// 根据fiber类型分发处理
switch (workInProgress.tag) {
case FunctionComponent:
return updateFunctionComponent(...);
case ClassComponent:
return updateClassComponent(...);
// ... 其他20+组件类型处理 ...
}
}
以下是例举的类型整理
updateFunctionComponent
updateFunctionComponent
是一个处理函数组件的函数,属于React协调过程的一部分。函数组件在React中是无状态的,但使用Hooks后可以有状态,所以需要处理Hooks相关的逻辑
Hooks处理核心函数renderWithHooks
function updateFunctionComponent(
current,
workInProgress,
Component,
nextProps: any,
renderLanes,
) {
// 开发环境校验逻辑
if (__DEV__) {
// 处理热重载后的组件类型变化
if (workInProgress.type !== workInProgress.elementType) {
// 校验更新后的组件propTypes
const innerPropTypes = Component.propTypes;
if (innerPropTypes) {
checkPropTypes(/*...*/);
}
}
}
// 上下文处理
let context;
if (!disableLegacyContext) {
// 获取未过滤的上下文
const unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
// 获取过滤后的上下文
context = getMaskedContext(workInProgress, unmaskedContext);
}
// 准备读取上下文
prepareToReadContext(workInProgress, renderLanes);
// 性能分析标记
if (enableSchedulingProfiler) {
markComponentRenderStarted(workInProgress);
}
// 核心渲染流程
let nextChildren;
if (__DEV__) {
// 开发模式下的严格模式检查
ReactCurrentOwner.current = workInProgress;
setIsRendering(true);
nextChildren = renderWithHooks(/*...*/);
// 严格模式二次渲染检测副作用
if (debugRenderPhaseSideEffectsForStrictMode) {
// ...
}
setIsRendering(false);
} else {
// 生产环境直接渲染
nextChildren = renderWithHooks(
current,
workInProgress,
Component,
nextProps,
context,
renderLanes,
);
}
// 优化策略:跳过无更新的组件
if (current !== null && !didReceiveUpdate) {
bailoutHooks(current, workInProgress, renderLanes);
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
// 协调子节点
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
renderWithHooks Hooks处理核心
该函数是React Hooks实现的核心部分之一
renderWithHooks
负责管理Hooks的生命周期,处理组件的挂载和更新,确保Hooks的顺序和状态正确,同时处理渲染过程中的更新和错误情况。这个函数在React的并发模式和Hooks机制中起着桥梁作用,连接了组件的渲染逻辑和Hooks的状态管理
export function renderWithHooks<Props, SecondArg>(
current: Fiber | null,
workInProgress: Fiber,
Component: (p: Props, arg: SecondArg) => any,
props: Props,
secondArg: SecondArg,
nextRenderLanes: Lanes,
): any {
renderLanes = nextRenderLanes;
currentlyRenderingFiber = workInProgress;
//重置 准备新的渲染过程
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
workInProgress.lanes = NoLanes;
//根据当前是挂载还是更新阶段,选择合适的Dispatcher,这在Hooks的mount和update阶段有不同的实现
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
//调用组件函数`Component`生成子元素
let children = Component(props, secondArg);
//检查是否存在渲染阶段的更新。如果存在,进入循环重新渲染,直到没有更多更新或达到限制次数
if (didScheduleRenderPhaseUpdateDuringThisPass) {
let numberOfReRenders: number = 0;
do {
didScheduleRenderPhaseUpdateDuringThisPass = false;
localIdCounter = 0;
if (numberOfReRenders >= RE_RENDER_LIMIT) {
throw new Error(
'Too many re-renders. React limits the number of renders to prevent ' +
'an infinite loop.',
);
}
numberOfReRenders += 1;
// Start over from the beginning of the list
currentHook = null;
workInProgressHook = null;
workInProgress.updateQueue = null;
ReactCurrentDispatcher.current = __DEV__
? HooksDispatcherOnRerenderInDEV
: HooksDispatcherOnRerender;
children = Component(props, secondArg);
} while (didScheduleRenderPhaseUpdateDuringThisPass);
}
//重置Dispatcher为`ContextOnlyDispatcher`
ReactCurrentDispatcher.current = ContextOnlyDispatcher;
//进行开发环境下的各种校验,比如Hook数量是否正确,静态标记是否一致等。最后处理上下文传播的延迟更新,并返回子元素
}
管理完hooks后,紧接这就到了核心的diff
算法比较函数了