performWork
1 )概述
- performWork 涉及到在调度完成,或者同步任务进来之后整个 root 节点链条如何更新
- 怎么更新一棵 Fiber 树,它的每一个节点是如何被遍历到,以及如何进行更新操作
- A. 在执行 performWork 时候,是否有 deadline 的区分
- deadline 是通过 reactschedule 它的一个时间片,更新的过程当中
- 产生的一个叫做 deadlineobject 的对象
- 它可以用来判断我们在一帧的渲染时间内留给react进行fiber树渲染的时间还有没有
- B.循环渲染root的条件
- 一个应用当中可能会有多个root节点
- 同时每一个root节点上面呢又会有不同优先级的任务产生
- 要循环去遍历各个不同的root节点
- 以及他们的不同的优先级的任务,然后按照优先级去一个个去更新
- 这个循环它如何建立,如何判断这个循环是否成立的条件
- C.超过时间片之后的一个处理
- 在deadline到了之后,就是我们这一帧的渲染时间已经到了
- 我们需要把js的执行权又交回给浏览器,这个时候又该怎么做
2 )源码文章来源:https://www.toymoban.com/news/detail-796852.html
定位到 packages/react-reconciler/src/ReactFiberScheduler.js文章来源地址https://www.toymoban.com/news/detail-796852.html
function performAsyncWork() {
try {
if (!shouldYieldToRenderer()) {
// The callback timed out. That means at least one update has expired.
// Iterate through the root schedule. If they contain expired work, set
// the next render expiration time to the current time. This has the effect
// of flushing all expired work in a single batch, instead of flushing each
// level one at a time.
if (firstScheduledRoot !== null) {
recomputeCurrentRendererTime();
let root: FiberRoot = firstScheduledRoot;
do {
didExpireAtExpirationTime(root, currentRendererTime);
// The root schedule is circular, so this is never null.
root = (root.nextScheduledRoot: any);
} while (root !== firstScheduledRoot);
}
}
performWork(NoWork, true);
} finally {
didYield = false;
}
}
function performSyncWork() {
performWork(Sync, null);
}
function performWork(minExpirationTime: ExpirationTime, dl: Deadline | null) {
deadline = dl;
// Keep working on roots until there's no more work, or until we reach
// the deadline.
findHighestPriorityRoot();
if (deadline !== null) {
recomputeCurrentRendererTime();
currentSchedulerTime = currentRendererTime;
if (enableUserTimingAPI) {
const didExpire = nextFlushedExpirationTime < currentRendererTime;
const timeout = expirationTimeToMs(nextFlushedExpirationTime);
stopRequestCallbackTimer(didExpire, timeout);
}
while (
nextFlushedRoot !== null &&
nextFlushedExpirationTime !== NoWork &&
(minExpirationTime === NoWork ||
minExpirationTime >= nextFlushedExpirationTime) &&
(!deadlineDidExpire || currentRendererTime >= nextFlushedExpirationTime)
) {
performWorkOnRoot(
nextFlushedRoot,
nextFlushedExpirationTime,
currentRendererTime >= nextFlushedExpirationTime,
);
findHighestPriorityRoot();
recomputeCurrentRendererTime();
currentSchedulerTime = currentRendererTime;
}
} else {
while (
nextFlushedRoot !== null &&
nextFlushedExpirationTime !== NoWork &&
(minExpirationTime === NoWork ||
minExpirationTime >= nextFlushedExpirationTime)
) {
performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, true);
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 (deadline !== null) {
callbackExpirationTime = NoWork;
callbackID = null;
}
// If there's work left over, schedule a new callback.
if (nextFlushedExpirationTime !== NoWork) {
scheduleCallbackWithExpirationTime(
((nextFlushedRoot: any): FiberRoot),
nextFlushedExpirationTime,
);
}
// Clean-up.
deadline = null;
deadlineDidExpire = false;
finishRendering();
}
-
performAsyncWork
,performSyncWork
,performWork
三个方法连在一起 - 对于
performSyncWork
直接 调用performWork(Sync, null);
就一行代码 -
performAsyncWork
就相对复杂,是通过 react scheduler 调度回来的- 参数是 deadline 对象
dl
- 本质上只有一个 if判断 和 执行 performWork 方法
- 在if判断中,
recomputeCurrentRendererTime
这个方法不涉及主要流程,跳过- 在找到 root 之后,执行
didExpireaTexpirationTime
标记在root节点上的一些变量// packages/react-reconciler/src/ReactFiberPendingPriority.js export function didExpireAtExpirationTime( root: FiberRoot, currentTime: ExpirationTime, ): void { const expirationTime = root.expirationTime; // 有任务,但任务过期 if (expirationTime !== NoWork && currentTime >= expirationTime) { // The root has expired. Flush all work up to the current time. root.nextExpirationTimeToWorkOn = currentTime; // 挂载 当前时间 作为 nextExpirationTimeToWorkOn 属性 } }
- 之后,一个do while 找最终的 root
- 在找到 root 之后,执行
- 之后,执行
performWork(NoWork, dl)
- NoWork 是 0
export const NoWork = 0
- 参数是 deadline 对象
- 在
performWork
中- 有两个参数,
minExpirationTime: ExpirationTime, dl: Deadline | null
-
findHighestPriorityRoot
这个方法function findHighestPriorityRoot() { let highestPriorityWork = NoWork; let highestPriorityRoot = null; // 这个if表示仍存在节点更新情况 if (lastScheduledRoot !== null) { let previousScheduledRoot = lastScheduledRoot; let root = firstScheduledRoot; // 存在 root 就进行循环 while (root !== null) { const remainingExpirationTime = root.expirationTime; // 这个if判断表示,是没有任何更新的 if (remainingExpirationTime === NoWork) { // This root no longer has work. Remove it from the scheduler. // TODO: This check is redudant, but Flow is confused by the branch // below where we set lastScheduledRoot to null, even though we break // from the loop right after. invariant( previousScheduledRoot !== null && lastScheduledRoot !== null, 'Should have a previous and last root. This error is likely ' + 'caused by a bug in React. Please file an issue.', ); // 这种情况,只有一个 root 节点 if (root === root.nextScheduledRoot) { // This is the only root in the list. root.nextScheduledRoot = null; firstScheduledRoot = lastScheduledRoot = null; break; } else if (root === firstScheduledRoot) { // 这时候 root 就没用了,可以删除了,获取 next // This is the first root in the list. const next = root.nextScheduledRoot; firstScheduledRoot = next; lastScheduledRoot.nextScheduledRoot = next; root.nextScheduledRoot = null; } else if (root === lastScheduledRoot) { // 这个时候,root是最后一个 // This is the last root in the list. lastScheduledRoot = previousScheduledRoot; lastScheduledRoot.nextScheduledRoot = firstScheduledRoot; root.nextScheduledRoot = null; break; } else { // 移除 中间的 root.nextScheduledRoot 节点 previousScheduledRoot.nextScheduledRoot = root.nextScheduledRoot; root.nextScheduledRoot = null; } root = previousScheduledRoot.nextScheduledRoot; } else { // 判断优先级,更新更高优先级 if ( highestPriorityWork === NoWork || remainingExpirationTime < highestPriorityWork ) { // Update the priority, if it's higher highestPriorityWork = remainingExpirationTime; highestPriorityRoot = root; } if (root === lastScheduledRoot) { break; } if (highestPriorityWork === Sync) { // Sync is highest priority by definition so // we can stop searching. break; } previousScheduledRoot = root; root = root.nextScheduledRoot; } } } // 处理更新后的 highestPriorityRoot 和 highestPriorityWork nextFlushedRoot = highestPriorityRoot; nextFlushedExpirationTime = highestPriorityWork; }
- 看下 deadine 不为 null 时的情况
- 这是异步的情况
- 主要看 while 里的
(!deadlineDidExpire || currentRendererTime >= nextFlushedExpirationTime)
- !deadlineDidExpire 表示时间片还有剩余时间
- nextFlushedExpirationTime 是现在要输出任务的过期时间
- currentRendererTime >= nextFlushedExpirationTime
- 说明 当前 render 时间 比 过期时间大,已经超时了,需要强制输出
- 之后执行 performWorkOnRoot
- 关于这个函数,主要关注第三个参数
- 默认是 true
- 对于异步情况,值为: currentRendererTime >= nextFlushedExpirationTime
- 任务过期,则为 true, 否则为 false
- 进入 这个函数
function performWorkOnRoot( root: FiberRoot, expirationTime: ExpirationTime, isExpired: 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. // deadline === null 是 Sync的情况,isExpired 是过期的情况 if (deadline === null || isExpired) { // 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). let finishedWork = root.finishedWork; if (finishedWork !== null) { // This root is already complete. We can commit it. completeRoot(root, finishedWork, expirationTime); // 有 finishedWork 直接 completeRoot } 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; // 处理 timeoutHandle 的情况,跳过 if (timeoutHandle !== noTimeout) { root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above cancelTimeout(timeoutHandle); } // 在这个条件下,不可中断 因为在上层if框架下,要么是 Sync的任务,要么是 过期的任务需要立即执行 const isYieldy = false; renderRoot(root, isYieldy, isExpired); finishedWork = root.finishedWork; if (finishedWork !== null) { // We've completed the root. Commit it. completeRoot(root, finishedWork, expirationTime); } } } else { // 这里匹配 Async 异步任务,和上面Sync的任务流程基本差不多 // Flush async work. let finishedWork = root.finishedWork; // 一开始进来判断这个 finishedWork,有可能在上一个时间片中,renderRoot执行完了,但是,没有时间去执行 completeRoot 了 // 需要再下次异步调度的时候进来,如果有 finishedWork 则先 completeRoot 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); } // 这个任务是可以中断的 const isYieldy = true; renderRoot(root, isYieldy, isExpired); finishedWork = root.finishedWork; // finishedWork 有可能为 null, 中断了就没有完成任务 if (finishedWork !== null) { // We've completed the root. Check the deadline one more time // before committing. // 先判断是否需要跳出,这时候时间片可能已经用完,如果没有用完,执行 completeRoot if (!shouldYield()) { // 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. // 需要跳出,则赋值 finishedWork,注意这里不执行 completeRoot,因为没有时间了,需要等待下个时间片进来才能执行 root.finishedWork = finishedWork; } } } } isRendering = false; }
- 执行
findHighestPriorityRoot
currentSchedulerTime = currentRendererTime
- 看下 deadine 为 null 时的情况
- 这是同步的情况
-
while(nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || minExpirationTime >= nextFlushedExpirationTime))
- 对于 perfromSyncWork 来说,minExpirationTime 是 1,1 >= nextFlushedExpirationTime 说明 只有 Sync(1)的情况,或者 NoWork,所以 nextFlushedExpirationTime 只有是1的情况
- 相当于在 perfromSyncWork 的时候,只会执行 root.expirationTime 是 Sync 的任务,也就是说是同步更新的更新,才会在这里继续执行,这样和 SyncWork 这函数名匹配
- 在这种情况下,调用
performWorkOnRoot
和findHighestPriorityRoot
执行掉 Sync的任务
- 有两个参数,
- 注意,在 performWork 上两个循环的判断条件
- 以及传入 performWorkOnRoot的第三个参数的意义
到了这里,关于React16源码: React中的performWork的源码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!