初窥React-7(updateContainer方法-p2)
来到了update入栈了 enqueueUpdate(current$1, update);
function enqueueUpdate(fiber, update) { var updateQueue = fiber.updateQueue; //取出当前的updateQueue if (updateQueue === null) { // Only occurs if the fiber has been unmounted. return; } var sharedQueue = updateQueue.shared; var pending = sharedQueue.pending; //update插入到fiber的updateQueue.shared队列当中... if (pending === null) { // This is the first update. Create a circular list. update.next = update; } else { //入队 c- > a b之间 update.next = pending.next; pending.next = update; } sharedQueue.pending = update; { if (currentlyProcessingQueue === sharedQueue && !didWarnUpdateInsideUpdate) { error('An update (setState, replaceState, or forceUpdate) was scheduled ' + 'from inside an update function. Update functions should be pure, ' + 'with zero side-effects. Consider using componentDidUpdate or a ' + 'callback.'); didWarnUpdateInsideUpdate = true; } } }
update对象创建完成后意味着需要对页面进行更新,会调用scheduleUpdateOnFiber进入调度
scheduleUpdateOnFiber(current$1, lane, eventTime);
function scheduleUpdateOnFiber(fiber, lane, eventTime) { checkForNestedUpdates(); warnAboutRenderPhaseUpdatesInDEV(fiber); var root = markUpdateLaneFromFiberToRoot(fiber, lane); if (root === null) { warnAboutUpdateOnUnmountedFiberInDEV(fiber); return null; } // Mark that the root has a pending update. markRootUpdated(root, lane, eventTime); if (root === workInProgressRoot) { // Received an update to a tree that's in the middle of rendering. Mark // that there was an interleaved update work on this root. Unless the // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render // phase update. In that case, we don't treat render phase updates as if // they were interleaved, for backwards compat reasons. { workInProgressRootUpdatedLanes = mergeLanes(workInProgressRootUpdatedLanes, lane); } if (workInProgressRootExitStatus === RootSuspendedWithDelay) { // The root already suspended with a delay, which means this render // definitely won't finish. Since we have a new update, let's mark it as // suspended now, right before marking the incoming update. This has the // effect of interrupting the current render and switching to the update. // TODO: Make sure this doesn't override pings that happen while we've // already started rendering. markRootSuspended$1(root, workInProgressRootRenderLanes); } } // TODO: requestUpdateLanePriority also reads the priority. Pass the // priority as an argument to that function and this one. var priorityLevel = getCurrentPriorityLevel(); if (lane === SyncLane) { //SyncLane 情况下,判断是否处在unbatched并且Render和Commit阶段 if ( // Check if we're inside unbatchedUpdates (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering (executionContext & (RenderContext | CommitContext)) === NoContext) { // Register pending interactions on the root to avoid losing traced interaction data. schedulePendingInteractions(root, lane); // This is a legacy edge case. The initial mount of a ReactDOM.render-ed // root inside of batchedUpdates should be synchronous, but layout updates // should be deferred until the end of the batch. performSyncWorkOnRoot(root);//比较重要其他篇幅会介绍,里面是render过程 } else { ensureRootIsScheduled(root, eventTime);//比较重要其他篇幅会介绍,调度决策逻辑,任务优先级在即将调度的时候会被计算 schedulePendingInteractions(root, lane); if (executionContext === NoContext) { // Flush the synchronous work now, unless we're already working or inside // a batch. This is intentionally inside scheduleUpdateOnFiber instead of // scheduleCallbackForFiber to preserve the ability to schedule a callback // without immediately flushing it. We only do this for user-initiated // updates, to preserve historical behavior of legacy mode. resetRenderTimer(); flushSyncCallbackQueue(); } } } else { //处理非Sync的场景 // Schedule a discrete update but only if it's not Sync. if ((executionContext & DiscreteEventContext) !== NoContext && ( // Only updates at user-blocking priority or greater are considered // discrete, even inside a discrete event. priorityLevel === UserBlockingPriority$2 || priorityLevel === ImmediatePriority$1)) { // This is the result of a discrete event. Track the lowest priority // discrete update per root so we can flush them early, if needed. if (rootsWithPendingDiscreteUpdates === null) { rootsWithPendingDiscreteUpdates = new Set([root]); } else { rootsWithPendingDiscreteUpdates.add(root); } } // Schedule other updates after in case the callback is sync. ensureRootIsScheduled(root, eventTime); schedulePendingInteractions(root, lane); } // We use this when assigning a lane for a transition inside // `requestUpdateLane`. We assume it's the same as the root being updated, // since in the common case of a single root app it probably is. If it's not // the same root, then it's not a huge deal, we just might batch more stuff // together more than necessary. mostRecentlyUpdatedRoot = root; }
两种模式的不同点:
- createRootImpl中传入的第二个参数不一样 一个是LegacyRoot一个是ConcurrentRoot
- requestUpdateLane中获取的lane的优先级不同
- 在函数scheduleUpdateOnFiber中根据不同优先级进入不同分支,legacy模式进入performSyncWorkOnRoot,concurrent模式会异步调度performConcurrentWorkOnRoot
// This is split into a separate function so we can mark a fiber with pending // work without treating it as a typical update that originates from an event; // e.g. retrying a Suspense boundary isn't an update, but it does schedule work // on a fiber. // 在markUpdateLaneFromFiberToRoot函数中会从触发更新的节点开始向上遍历到rootFiber,遍历的过程会处理节点的优先级 function markUpdateLaneFromFiberToRoot(sourceFiber, lane) { // Update the source fiber's lanes sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane); var alternate = sourceFiber.alternate; if (alternate !== null) { alternate.lanes = mergeLanes(alternate.lanes, lane); } { if (alternate === null && (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags) { warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber); } } // Walk the parent path to the root and update the child expiration time. var node = sourceFiber; var parent = sourceFiber.return; while (parent !== null) { parent.childLanes = mergeLanes(parent.childLanes, lane);//更新parent的childLanes alternate = parent.alternate; //alternate指向parent.alternate if (alternate !== null) { alternate.childLanes = mergeLanes(alternate.childLanes, lane); //更新parent.alternate的childLanes. } else { { if ((parent.flags & (Placement | Hydrating)) !== NoFlags) { warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber); } } } node = parent; //继续向上遍历 parent = parent.return; } if (node.tag === HostRoot) { var root = node.stateNode; return root; } else { return null; } }
function markRootUpdated(root, updateLane, eventTime) { root.pendingLanes |= updateLane;
// TODO: Theoretically, any update to any lane can unblock any other lane. But // it's not practical to try every single possible combination. We need a // heuristic to decide which lanes to attempt to render, and in which batches. // For now, we use the same heuristic as in the old ExpirationTimes model: // retry any lane at equal or lower priority, but don't try updates at higher // priority without also including the lower priority updates. This works well // when considering updates across different priority levels, but isn't // sufficient for updates within the same priority, since we want to treat // those updates as parallel. // Unsuspend any update at equal or lower priority. var higherPriorityLanes = updateLane - 1; // Turns 0b1000 into 0b0111 root.suspendedLanes &= higherPriorityLanes; root.pingedLanes &= higherPriorityLanes; var eventTimes = root.eventTimes; var index = laneToIndex(updateLane); // We can always overwrite an existing timestamp because we prefer the most // recent event, and we assume time is monotonically increasing. eventTimes[index] = eventTime; }
Just Do It