在 React 源碼中,scheduleUpdateOnFiber 是所有任務的唯一入口方法。我們前面分析 useState 的實現原理章節中,我們可以清晰的知道,當我們調用 dispatchSetState 時,最終會調用該入口方法。
scheduleUpdateOnFiber 主要用于觸發一個 Fiber 節點上的調度更新任務,該函數里主要有兩個核心邏輯。
// Mark that the root has a pending update.// 標記 root 上有一個更新任務markRootUpdated(root, lane, eventTime);ensureRootIsScheduled(root, eventTime);
markRootUpdated 的邏輯如下,簡單了解一下即可。
export function markRootUpdated( root: FiberRoot, updateLane: Lane, eventTime: number,) { // 設置本次更新的優先級 root.pendingLanes |= updateLane; // 重置 root 應用根節點的優先級 if (updateLane !== IdleLane) { // 由 Suspence 而掛起的 update 對應的 lane 集合 root.suspendedLanes = NoLanes; // 由請求成功,Suspence 取消掛起的 update 對應的 Lane 集合 root.pingedLanes = NoLanes; } const eventTimes = root.eventTimes; const index = laneToIndex(updateLane); eventTimes[index] = eventTime;}
ensureRootIsScheduled 的主要目的要確保 root 根節點被調度。在該邏輯中,會根據 root.pendingLanes 信息計算出本次更新的 Lanes: nextLanes。
const nextLanes = getNextLanes( root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,);
然后根據 nextLanes 計算出本批次集合中優先級最高的 Lane,作為本地任務的優先級。
// We use the highest priority lane to represent the priority of the callback.const newCallbackPriority = getHighestPriorityLane(nextLanes);
后續的邏輯就是取出當前已存在的調度優先級,與 newCallbackPriority 進行對比,根據對比結果來執行不同的更新方法。當該值等于 SyncLane 時,表示為同步更新。
同步優先級例如點擊事件。
然后會判斷是否支持微任務更新,如果不支持最后會執行 scheduleCallback。
if (newCallbackPriority === SyncLane) { if (supportsMicrotasks) { // Flush the queue in a microtask. if (__DEV__ && ReactCurrentActQueue.current !== null) { // Inside `act`, use our internal `act` queue so that these get flushed // at the end of the current scope even when using the sync version // of `act`. ReactCurrentActQueue.current.push(flushSyncCallbacks); } else { scheduleMicrotask(() => { // In Safari, appending an iframe forces microtasks to run. // https://github.com/facebook/react/issues/22459 // We don't support running callbacks in the middle of render // or commit so we need to check against that. if ( (executionContext & (RenderContext | CommitContext)) === NoContext ) { // Note that this would still prematurely flush the callbacks // if this happens outside render or commit phase (e.g. in an event). flushSyncCallbacks(); } }); } } else { // Flush the queue in an Immediate task. scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks); }}
scheduleSyncCallback 的邏輯,也就是同步任務的調度非常簡單,就是將執行同步任務的回調添加到一個同步隊列 syncQueue 中。
export function scheduleSyncCallback(callback: SchedulerCallback) { // Push this callback into an internal queue. We'll flush these either in // the next tick, or earlier if something calls `flushSyncCallbackQueue`. if (syncQueue === null) { syncQueue = [callback]; } else { // Push onto existing queue. Don't need to schedule a callback because // we already scheduled one when we created the queue. syncQueue.push(callback); }}
這里的 callback 是之前傳入的 performSyncWorkOnRoot,這是用來執行同步更新任務的方法。他的邏輯主要包括:
function performSyncWorkOnRoot(root) { ... let exitStatus = renderRootSync(root, lanes); ... root.finishedWork = finishedWork; root.finishedLanes = lanes; commitRoot( root, workInProgressRootRecoverableErrors, workInProgressTransitions, ); ensureRootIsScheduled(root, now()); return null;}
workLoopSync 的邏輯也非常簡單,如下:
function workLoopSync() { // Already timed out, so perform work without checking if we need to yield. while (workInProgress !== null) { performUnitOfWork(workInProgress); }}
在 performUnitOfWork 中,會調用 beginWork 方法開始創建 Fiber 節點。
var next = beginWork( current, unitOfWork, subtreeRenderLanes);
同步更新的過程比較簡單,從 scheduleUpdateOnFiber 到 beginWork 這中間的流程里,大多數邏輯都在進行各種不同情況的判斷,因此源碼看上去比較吃力,實際邏輯并不是很重要,簡單了解即可,重要的是 beginWork 創建 Fiber 節點的方法,這跟我們之前文章里提到過的優化策略是一致的。
本文鏈接:http://www.tebozhan.com/showinfo-26-87990-0.html一圖看懂 React 源碼中的同步更新邏輯
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com