performWork
1 )概述
- performWork 涉及到在調(diào)度完成,或者同步任務(wù)進來之后整個 root 節(jié)點鏈條如何更新
- 怎么更新一棵 Fiber 樹,它的每一個節(jié)點是如何被遍歷到,以及如何進行更新操作
- A. 在執(zhí)行 performWork 時候,是否有 deadline 的區(qū)分
- deadline 是通過 reactschedule 它的一個時間片,更新的過程當中
- 產(chǎn)生的一個叫做 deadlineobject 的對象
- 它可以用來判斷我們在一幀的渲染時間內(nèi)留給react進行fiber樹渲染的時間還有沒有
- B.循環(huán)渲染root的條件
- 一個應(yīng)用當中可能會有多個root節(jié)點
- 同時每一個root節(jié)點上面呢又會有不同優(yōu)先級的任務(wù)產(chǎn)生
- 要循環(huán)去遍歷各個不同的root節(jié)點
- 以及他們的不同的優(yōu)先級的任務(wù),然后按照優(yōu)先級去一個個去更新
- 這個循環(huán)它如何建立,如何判斷這個循環(huán)是否成立的條件
- C.超過時間片之后的一個處理
- 在deadline到了之后,就是我們這一幀的渲染時間已經(jīng)到了
- 我們需要把js的執(zhí)行權(quán)又交回給瀏覽器,這個時候又該怎么做
2 )源碼文章來源:http://www.zghlxwxcb.cn/news/detail-796852.html
定位到 packages/react-reconciler/src/ReactFiberScheduler.js文章來源地址http://www.zghlxwxcb.cn/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
直接 調(diào)用performWork(Sync, null);
就一行代碼 -
performAsyncWork
就相對復(fù)雜,是通過 react scheduler 調(diào)度回來的- 參數(shù)是 deadline 對象
dl
- 本質(zhì)上只有一個 if判斷 和 執(zhí)行 performWork 方法
- 在if判斷中,
recomputeCurrentRendererTime
這個方法不涉及主要流程,跳過- 在找到 root 之后,執(zhí)行
didExpireaTexpirationTime
標記在root節(jié)點上的一些變量// packages/react-reconciler/src/ReactFiberPendingPriority.js export function didExpireAtExpirationTime( root: FiberRoot, currentTime: ExpirationTime, ): void { const expirationTime = root.expirationTime; // 有任務(wù),但任務(wù)過期 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 之后,執(zhí)行
- 之后,執(zhí)行
performWork(NoWork, dl)
- NoWork 是 0
export const NoWork = 0
- 參數(shù)是 deadline 對象
- 在
performWork
中- 有兩個參數(shù),
minExpirationTime: ExpirationTime, dl: Deadline | null
-
findHighestPriorityRoot
這個方法function findHighestPriorityRoot() { let highestPriorityWork = NoWork; let highestPriorityRoot = null; // 這個if表示仍存在節(jié)點更新情況 if (lastScheduledRoot !== null) { let previousScheduledRoot = lastScheduledRoot; let root = firstScheduledRoot; // 存在 root 就進行循環(huán) 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 節(jié)點 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 節(jié)點 previousScheduledRoot.nextScheduledRoot = root.nextScheduledRoot; root.nextScheduledRoot = null; } root = previousScheduledRoot.nextScheduledRoot; } else { // 判斷優(yōu)先級,更新更高優(yōu)先級 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 是現(xiàn)在要輸出任務(wù)的過期時間
- currentRendererTime >= nextFlushedExpirationTime
- 說明 當前 render 時間 比 過期時間大,已經(jīng)超時了,需要強制輸出
- 之后執(zhí)行 performWorkOnRoot
- 關(guān)于這個函數(shù),主要關(guān)注第三個參數(shù)
- 默認是 true
- 對于異步情況,值為: currentRendererTime >= nextFlushedExpirationTime
- 任務(wù)過期,則為 true, 否則為 false
- 進入 這個函數(shù)
function performWorkOnRoot( root: FiberRoot, expirationTime: ExpirationTime, isExpired: boolean, // 是否過期,這個方法里一定有針對過期任務(wù)的強制更新 ) { invariant( !isRendering, 'performWorkOnRoot was called recursively. This error is likely caused ' + 'by a bug in React. Please file an issue.', ); isRendering = true; // 注意這里和函數(shù)結(jié)束 // 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的任務(wù),要么是 過期的任務(wù)需要立即執(zhí)行 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 異步任務(wù),和上面Sync的任務(wù)流程基本差不多 // Flush async work. let finishedWork = root.finishedWork; // 一開始進來判斷這個 finishedWork,有可能在上一個時間片中,renderRoot執(zhí)行完了,但是,沒有時間去執(zhí)行 completeRoot 了 // 需要再下次異步調(diào)度的時候進來,如果有 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); } // 這個任務(wù)是可以中斷的 const isYieldy = true; renderRoot(root, isYieldy, isExpired); finishedWork = root.finishedWork; // finishedWork 有可能為 null, 中斷了就沒有完成任務(wù) if (finishedWork !== null) { // We've completed the root. Check the deadline one more time // before committing. // 先判斷是否需要跳出,這時候時間片可能已經(jīng)用完,如果沒有用完,執(zhí)行 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,注意這里不執(zhí)行 completeRoot,因為沒有時間了,需要等待下個時間片進來才能執(zhí)行 root.finishedWork = finishedWork; } } } } isRendering = false; }
- 執(zhí)行
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 的時候,只會執(zhí)行 root.expirationTime 是 Sync 的任務(wù),也就是說是同步更新的更新,才會在這里繼續(xù)執(zhí)行,這樣和 SyncWork 這函數(shù)名匹配
- 在這種情況下,調(diào)用
performWorkOnRoot
和findHighestPriorityRoot
執(zhí)行掉 Sync的任務(wù)
- 有兩個參數(shù),
- 注意,在 performWork 上兩個循環(huán)的判斷條件
- 以及傳入 performWorkOnRoot的第三個參數(shù)的意義
到了這里,關(guān)于React16源碼: React中的performWork的源碼實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!