function performWorkOnRoot(
root: FiberRoot,
expirationTime: ExpirationTime,
isYieldy: boolean,
) {
invariant(
!isRendering,
'performWorkOnRoot was called recursively. This error is likely caused ' +
'by a bug in React. Please file an issue.',
);
// 要开始渲染了
isRendering = true;
// Check if this is async work or sync/expired work.
// isYieldy=true 表示异步的
// 同步任务
if (!isYieldy) {
// Flush work without yielding.
// TODO: Non-yieldy work does not necessarily imply expired work. A renderer
// may want to perform some work without yielding, but also without
// requiring the root to complete (by triggering placeholders).
// update队列
let finishedWork = root.finishedWork;
if (finishedWork !== null) {
// This root is already complete. We can commit it.
// 存在任务了
// finishedWork存在,进行commit了
completeRoot(root, finishedWork, expirationTime);
} else {
root.finishedWork = null;
// If this root previously suspended, clear its existing timeout, since
// we're about to try rendering again.
// 如果这个Root,之前有暂停,清除掉这个定时器,会尝试继续渲染
const timeoutHandle = root.timeoutHandle;
if (timeoutHandle !== noTimeout) {
root.timeoutHandle = noTimeout;
// $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
cancelTimeout(timeoutHandle);
}
// 继续调度
renderRoot(root, isYieldy);
finishedWork = root.finishedWork;
if (finishedWork !== null) {
// We've completed the root. Commit it.
// 判断是否可以执行commit
completeRoot(root, finishedWork, expirationTime);
}
}
} else {
// Flush async work.
let finishedWork = root.finishedWork;
if (finishedWork !== null) {
// This root is already complete. We can commit it.
completeRoot(root, finishedWork, expirationTime);
} else {
root.finishedWork = null;
// If this root previously suspended, clear its existing timeout, since
// we're about to try rendering again.
const timeoutHandle = root.timeoutHandle;
if (timeoutHandle !== noTimeout) {
root.timeoutHandle = noTimeout;
// $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
cancelTimeout(timeoutHandle);
}
renderRoot(root, isYieldy);
finishedWork = root.finishedWork;
if (finishedWork !== null) {
// We've completed the root. Check the if we should yield one more time
// before committing.
if (!shouldYieldToRenderer()) {
// Still time left. Commit the root.
completeRoot(root, finishedWork, expirationTime);
} else {
// There's no time left. Mark this root as complete. We'll come
// back and commit it later.
root.finishedWork = finishedWork;
}
}
}
}
isRendering = false;
}
一进来,将 isRendering 设置为 true,然后判断是否存在 finishedWork,如果存在,执行 completeRoot 方法,开始更新 DOM
function completeRoot(
root: FiberRoot,
finishedWork: Fiber,
expirationTime: ExpirationTime,
): void {
// Check if there's a batch that matches this expiration time.
const firstBatch = root.firstBatch;
if (firstBatch !== null && firstBatch._expirationTime >= expirationTime) {
if (completedBatches === null) {
completedBatches = [firstBatch];
} else {
completedBatches.push(firstBatch);
}
if (firstBatch._defer) {
// This root is blocked from committing by a batch. Unschedule it until
// we receive another update.
root.finishedWork = finishedWork;
root.expirationTime = NoWork;
return;
}
}
// Commit the root.
root.finishedWork = null;
// Check if this is a nested update (a sync update scheduled during the
// commit phase).
if (root === lastCommittedRootDuringThisBatch) {
// If the next root is the same as the previous root, this is a nested
// update. To prevent an infinite loop, increment the nested update count.
nestedUpdateCount++;
} else {
// Reset whenever we switch roots.
lastCommittedRootDuringThisBatch = root;
nestedUpdateCount = 0;
}
runWithPriority(ImmediatePriority, () => {
// 把更新渲染到页面上
commitRoot(root, finishedWork);
});
}
由于 commitRoot 这个方法比较长,将放到下次内容里说明
如果不存在 finishedWork,判断是否之前有被暂停的任务,如果有,清除定时器,生成 finishedWork,继续渲染,如果没有,还是走 completeRoot 方法,更新 DOm,渲染结束,将 isRendering 设置为 false
function performSyncWork() {
performWork(Sync, false);
}
function performWork(minExpirationTime: ExpirationTime, isYieldy: boolean) {
// Keep working on roots until there's no more work, or until there's a higher
// priority event.
// nextFlushedRoot = highestPriorityRoot;
// nextFlushedExpirationTime = highestPriorityWork;
// 找出优先级最高的root
findHighestPriorityRoot();
// (performSyncWork | performAsyncWork ) isYieldy=true 是异步
if (isYieldy) {
// 重新计算时间
recomputeCurrentRendererTime();
currentSchedulerTime = currentRendererTime;
// __DEV__
if (enableUserTimingAPI) {
const didExpire = nextFlushedExpirationTime > currentRendererTime;
const timeout = expirationTimeToMs(nextFlushedExpirationTime);
stopRequestCallbackTimer(didExpire, timeout);
}
// 存在任务,任务过期时间不为空闲, 过期时间大于minExpirationTime
// 要么任务完成, 要么时间用尽
while (
nextFlushedRoot !== null &&
nextFlushedExpirationTime !== NoWork &&
minExpirationTime <= nextFlushedExpirationTime &&
!(didYield && currentRendererTime > nextFlushedExpirationTime)
) {
performWorkOnRoot(
nextFlushedRoot,
nextFlushedExpirationTime,
currentRendererTime > nextFlushedExpirationTime,
);
findHighestPriorityRoot();
recomputeCurrentRendererTime();
currentSchedulerTime = currentRendererTime;
}
} else {
// 存在任务,任务过期时间不为空闲, 过期时间大于minExpirationTime
// 要么任务完成, 要么时间用尽
while (
nextFlushedRoot !== null &&
nextFlushedExpirationTime !== NoWork &&
minExpirationTime <= nextFlushedExpirationTime
) {
performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
findHighestPriorityRoot();
}
}
// We're done flushing work. Either we ran out of time in this callback,
// or there's no more work left with sufficient priority.
// If we're inside a callback, set this to false since we just completed it.
if (isYieldy) {
callbackExpirationTime = NoWork;
callbackID = null;
}
// If there's work left over, schedule a new callback.
if (nextFlushedExpirationTime !== NoWork) {
scheduleCallbackWithExpirationTime(
((nextFlushedRoot: any): FiberRoot),
nextFlushedExpirationTime,
);
}
// Clean-up.
finishRendering();
}
找出优先级最高的 root,递归 root 下所有 Fiber,执行 performWorkOnRoot 方法,其实就是上面的非批处理任务
function scheduleCallbackWithExpirationTime(
root: FiberRoot,
expirationTime: ExpirationTime,
) {
// 判断是否还有正在跑的任务
if (callbackExpirationTime !== NoWork) {
// A callback is already scheduled. Check its expiration time (timeout).
// 判断任务优先级, 优先级低的任务直接return
if (expirationTime < callbackExpirationTime) {
// Existing callback has sufficient timeout. Exit.
return;
} else {
// 清空之前的callbackID
if (callbackID !== null) {
// Existing callback has insufficient timeout. Cancel and schedule a
// new one.
// 现有的回调没有足够的超时。 取消并安排一个新的。
cancelDeferredCallback(callbackID);
}
}
// The request callback timer is already running. Don't start a new one.
} else {
startRequestCallbackTimer();
}
// 设置新的callback和callbackExiporationTime
callbackExpirationTime = expirationTime;
const currentMs = now() - originalStartTimeMs;
const expirationTimeMs = expirationTimeToMs(expirationTime);
// 判断是否超时
const timeout = expirationTimeMs - currentMs;
// 生成一个 callbackID,用于关闭任务
// scheduledCallback = callback;
// 把callBack为performAsyncWork 赋给 scheduledCallback,
// scheduleDeferredCallback = unstable_scheduleCallback => requestHostCallback => requestAnimationFrame
callbackID = scheduleDeferredCallback(performAsyncWork, {timeout});
}
首先判断是否存在正在执行的任务,如果存在,判断当前任务有无足够时间,如果时间足够,继续执行,如果时间不够,调用 cancelDeferredCallback 方法,取消之前的 callbackID,重新生成一个
如果不存在正在执行的任务,调用 scheduleDeferredCallback 方法