Reconciler是如何运作的
date
Feb 13, 2022
slug
react-reconciler
status
Published
tags
React
源码
summary
从源码解析Reconciler运作原理
type
Post
本文基于React17.0.0 legacy mode
主流程概况
在React中,
reconciler
的作用就像连接器,连接着react
和scheduler
react-reconciler
主要起到了如下作用:- 向外提供入口:如
scheduleUpdateOnFiber
,updateContainer
等
- 与
scheduler
协作,根据优先级构建出task
,等待scheduler
来执行回调(scheduleSyncCallback)
- 构建
fiber tree
,与react-dom
创造出DOM(renderRootSync)
- 与
react-dom
渲染DOM(commitRoot)
scheduleUpdateOnFiber
在React是如何启动的中,
scheduleUpdateOnFiber
在updateContainer
中被调度,进入reconciler
流程。其实在关于fiber
的操作中,无论是挂载或更新,scheduleUpdateOnFiber
最终都会被调度。进入
scheduleUpdateOnFiber
后有两种情况- 本次为同步更新,且当前不在
render
阶段,直接调用performSyncWorkOnRoot
执行同步任务
- 当前有更新任务在进行,由于无法打断,调用
ensureRootIsScheduled
,目的是复用在更新的任务,让这个已有的任务把本次更新给做了
export function scheduleUpdateOnFiber(
fiber: Fiber,
lane: Lane,
eventTime: number,
) {
// 第一步,检查是否有无限更新, 例如在render函数中调用了setState
checkForNestedUpdates();
// 第二步,向上收集fiber.childLanes
const root = markUpdateLaneFromFiberToRoot(fiber, lane);
if (root === null) {
return null;
}
// 第三步,在root上标记更新,将update的lane放到root.pendingLanes
markRootUpdated(root, lane, eventTime);
if (root === workInProgressRoot) {
// workInProgressRoo存在,意味着是当前根节点触发的更新
if (
(executionContext & RenderContext) === NoContext
) {
workInProgressRootUpdatedLanes = mergeLanes(
workInProgressRootUpdatedLanes,
lane,
);
}
if (workInProgressRootExitStatus === RootSuspendedWithDelay) {
markRootSuspended(root, workInProgressRootRenderLanes);
}
}
// 根据Scheduler的优先级获取到对应的React优先级
const priorityLevel = getCurrentPriorityLevel();
if (lane === SyncLane) { // 0b0000000000000000000000000000001
// 根据Scheduler的优先级获取到对应的React优先级
if (
(executionContext & LegacyUnbatchedContext) !== NoContext &&
// 判断是否不在render过程中
(executionContext & (RenderContext | CommitContext)) === NoContext
) {
// 如果是本次更新是同步的(lane === SyncLane),并且当前还未渲染,意味着主线程空闲,并没有React的
// 更新任务在执行,那么调用performSyncWorkOnRoot开始执行同步任务
schedulePendingInteractions(root, lane);
performSyncWorkOnRoot(root);
} else {
// 如果当前有React更新任务正在进行,而且因为无法打断,所以调用ensureRootIsScheduled,
// 目的是去复用已经在更新的任务,让这个已有的任务把这次更新顺便做了
ensureRootIsScheduled(root, eventTime);
schedulePendingInteractions(root, lane);
// 通过判断 executionContext 是否等于 NoContext 来确定当前更新流程是否在 React 事件流中
// 如果不在(NoContext),直接调用 flushSyncCallbackQueue 更新
if (executionContext === NoContext) {
resetRenderTimer();
flushSyncCallbackQueue();
}
}
} else {
// 异步操作
if (
// 是否是用户事件触发的上下文
(executionContext & DiscreteEventContext) !== NoContext && // DiscreteEventContext = 0b0000100;
// UserBlockingPriority = 98, ImmediatePriority = 99
[UserBlockingSchedulerPriority, ImmediateSchedulerPriority].includes(priorityLevel)
) {
if (rootsWithPendingDiscreteUpdates === null) {
rootsWithPendingDiscreteUpdates = new Set([root]);
} else {
rootsWithPendingDiscreteUpdates.add(root);
}
}
ensureRootIsScheduled(root, eventTime);
schedulePendingInteractions(root, lane);
}
mostRecentlyUpdatedRoot = root;
}
ensureRootIsScheduled
接下来看看
ensureRootIsScheduled
:- 前半部分判断了是否需要重新注册新的调度,如果不需要则会return
- 走到后半部分,表示需要注册新的调度,则需要判断当前mode,来将
performSyncWorkOnRoot
或performConcurrentWorkOnRoot
作为回调传入scheduleCallback
中,等待scheduler
执行
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
// 获取旧任务,对应task上的callback,代表当前根节点正在被调度的任务
const existingCallbackNode = root.callbackNode;
// 记录任务的过期时间,检查是否有过期任务,有则立即将它放到root.expiredLanes,
// 便于接下来将这个任务以同步模式立即调度
markStarvedLanesAsExpired(root, currentTime);
// 获取renderLanes,顺便计算return_highestLanePriority,也即是下面的newCallbackPriority
const nextLanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
);
const newCallbackPriority = returnNextLanesPriority();
if (nextLanes === NoLanes) {
// 如果渲染优先级为空,则不需要调度
if (existingCallbackNode !== null) {
// 如果存在旧任务那么就取消掉
// 即existingCallbackNode.callback = null
cancelCallback(existingCallbackNode);
// 然后root上相应置空
root.callbackNode = null;
root.callbackPriority = NoLanePriority;
}
return;
}
// 如果存在旧任务,那么看一下能否复用
if (existingCallbackNode !== null) {
// 获取旧任务的优先级
const existingCallbackPriority = root.callbackPriority;
// 如果新旧任务的优先级相同,则无需调度,如多次调用setState:
// onClick = () => {
// setState(1);
// setState(2);
//}
if (existingCallbackPriority === newCallbackPriority) {
return;
}
// 代码执行到这里说明新任务的优先级高于旧任务的优先级,取消掉旧任务,实现高优先级任务插队
cancelCallback(existingCallbackNode);
}
// 调度一个新任务
let newCallbackNode;
if (newCallbackPriority === SyncLanePriority) {
// 若新任务的优先级(newCallbackPriority)为同步优先级(SyncLanePriority),则同步调度
// 传统的同步渲染和过期任务会走这里
// 同步渲染模式
newCallbackNode = scheduleSyncCallback(
performSyncWorkOnRoot.bind(null, root),
);
} else if (newCallbackPriority === SyncBatchedLanePriority) {
// 同步模式到concurrent模式的过渡模式:blocking模式会走这里
newCallbackNode = scheduleCallback(
ImmediateSchedulerPriority,
performSyncWorkOnRoot.bind(null, root),
);
} else {
// 否则就是concurrent模式
// 将本次更新任务的优先级转化为调度优先级
// schedulerPriorityLevel为调度优先级
const schedulerPriorityLevel = lanePriorityToSchedulerPriority(
newCallbackPriority,
);
// concurrent模式
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root),
);
}
// 更新root上的任务优先级和任务,以便下次发起调度时候可以获取到
root.callbackPriority = newCallbackPriority;
root.callbackNode = newCallbackNode;
}
performSyncWorkOnRoot
performSyncWorkOnRoot
中- 会先做一些前置处理:清除上一次更新未完成的
effects
、处理优先级相关、处理hydrate
相关等
- 调用
renderRootSync
,构建fiber tree
(render阶段)
- 调用
commitRoot
,渲染DOM(commit阶段)
function performSyncWorkOnRoot(root) {
invariant(
(executionContext & (RenderContext | CommitContext)) === NoContext,
'Should not already be working.',
);
// 清除未执行完的effects
flushPassiveEffects();
// 优先级相关
let lanes;
let exitStatus;
if (
root === workInProgressRoot &&
includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)
) {
lanes = workInProgressRootRenderLanes;
// 构造fiber tree
exitStatus = renderRootSync(root, lanes);
if (
includesSomeLane(
workInProgressRootIncludedLanes,
workInProgressRootUpdatedLanes,
)
) {
lanes = getNextLanes(root, lanes);
exitStatus = renderRootSync(root, lanes);
}
} else {
lanes = getNextLanes(root, NoLanes);
// 构造fiber tree
exitStatus = renderRootSync(root, lanes);
}
if (root.tag !== LegacyRoot && exitStatus === RootErrored) {
executionContext |= RetryAfterError;
if (root.hydrate) {
root.hydrate = false;
clearContainer(root.containerInfo);
}
lanes = getLanesToRetrySynchronouslyOnError(root);
if (lanes !== NoLanes) {
exitStatus = renderRootSync(root, lanes);
}
}
if (exitStatus === RootFatalErrored) {
const fatalError = workInProgressRootFatalError;
prepareFreshStack(root, NoLanes);
markRootSuspended(root, lanes);
ensureRootIsScheduled(root, now());
throw fatalError;
}
const finishedWork: Fiber = (root.current.alternate: any);
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
// 渲染DOM
commitRoot(root);
// 结束前再检查下是否有新的更新需要重新发起调度
ensureRootIsScheduled(root, now());
return null;
}
renderRootSync
renderRootSync
通过一个死循环调用workLoopSync
function renderRootSync(root: FiberRoot, lanes: Lanes) {
const prevExecutionContext = executionContext;
executionContext |= RenderContext;
const prevDispatcher = pushDispatcher();
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
prepareFreshStack(root, lanes);
startWorkOnPendingInteractions(root, lanes);
}
const prevInteractions = pushInteractions(root);
do {
try {
workLoopSync();
break;
} catch (thrownValue) {
handleError(root, thrownValue);
}
} while (true);
resetContextDependencies();
if (enableSchedulerTracing) {
popInteractions(((prevInteractions: any): Set<Interaction>));
}
executionContext = prevExecutionContext;
popDispatcher(prevDispatcher);
if (workInProgress !== null) {
// This is a sync render, so we should have finished the whole tree.
invariant(
false,
'Cannot commit an incomplete root. This error is likely caused by a ' +
'bug in React. Please file an issue.',
);
}
workInProgressRoot = null;
workInProgressRootRenderLanes = NoLanes;
return workInProgressRootExitStatus;
}
commitRoot
在
render
阶段结束后,进入commit
阶段,内部又分为三个阶段before mutaion
(执行DOM操作前)处理effectList
中SnapShot
,Passive
tag的fiber
mutaion
(执行DOM操作)处理Placement
,Update
,Deletion
,Hydrating
tag的fiber
layout
(执行DOM操作后)处理Update
,Callback
tag的fiber
至此,reconciler的主要流程就结束了。
render与commit核心原理
- 更新中