第一次加载页面

发生事件点击后



commitRoot(root)
root.current
// 包含有 effect fiber 环形链表
root.current.nextEffect
root.current.firstEffect
root.current.lastEffect
卡颂老师课件
https://www.bilibili.com/video/BV1Ki4y1u7Vr?from=search&seid=8355098943507069182
React16架构可以分为三层:
- Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler (requestIdleCallback放弃使用)
- Reconciler(协调器)—— 负责找出变化的组件
- Renderer(渲染器)—— 负责将变化的组件渲染到页面上
- Reconciler工作的阶段被称为render阶段。因为在该阶段会调用组件的render方法。
- Renderer工作的阶段被称为commit阶段。就像你完成一个需求的编码后执行git commit提交代码。commit阶段会把render阶段提交的信息渲染在页面上。
- render与commit阶段统称为work,即React在工作中。相对应的,如果任务正在Scheduler内调度,就不属于work。

相关执行流程博文
https://segmentfault.com/a/1190000037447202
代码主线跟踪
// 以 useState 为例 代码如下:import { useState } from "react";import "./styles.css";export default function App() {const [state, setState] = useState(0);return (<div className="App"><h1 onClick={() => setState(state + 1)}>Hello CodeSandbox state:{state}</h1><h2>Start editing to see some magic happen!</h2></div>);}// setState -> dispatchAction->创建 update ->scheduleUpdateOnFiber-> diff 算法(与其他scheduleUpdateOnFiber抢优先级) ->得出需要更新的 dom(新增,修改,删除)// ->操作完了以后,render 阶段结束// -> 然后进入 commit 阶段 执行 commitRoot(root)同步方法/** 相关代码引用 HooksDispatcherOnMount 另外还有 HooksDispatcherOnUpdate*/const HooksDispatcherOnMount: Dispatcher = {readContext,useCallback: mountCallback,useContext: readContext,useEffect: mountEffect,useImperativeHandle: mountImperativeHandle,useLayoutEffect: mountLayoutEffect,useMemo: mountMemo,useReducer: mountReducer,useRef: mountRef,useState: mountState,useDebugValue: mountDebugValue,useDeferredValue: mountDeferredValue,useTransition: mountTransition,useMutableSource: mountMutableSource,useOpaqueIdentifier: mountOpaqueIdentifier,unstable_isNewReconciler: enableNewReconciler,};function mountState<S>(initialState: (() => S) | S,): [S, Dispatch<BasicStateAction<S>>] {const hook = mountWorkInProgressHook();if (typeof initialState === 'function') {// $FlowFixMe: Flow doesn't like mixed typesinitialState = initialState();}hook.memoizedState = hook.baseState = initialState;const queue = (hook.queue = {pending: null,interleaved: null,lanes: NoLanes,dispatch: null,lastRenderedReducer: basicStateReducer,lastRenderedState: (initialState: any),});const dispatch: Dispatch<BasicStateAction<S>,> = (queue.dispatch = (dispatchAction.bind(null,currentlyRenderingFiber,queue,): any));return [hook.memoizedState, dispatch];}function dispatchAction(fiber, queue, action){// ....构建相关 fiber 参数scheduleUpdateOnFiber(fiber, lane, eventTime)}function FiberNode (tag, key) {// 节点 key,主要用于了优化列表 diffthis.key = key// 节点类型;FunctionComponent: 0, ClassComponent: 1, HostRoot: 3 ...this.tag = tag// 子节点this.child = null// 父节点this.return = null// 兄弟节点this.sibling = null// 更新队列,用于暂存 setState 的值this.updateQueue = null// 新传入的 propsthis.pendingProps = pendingProps;// 之前的 propsthis.memoizedProps = null;// 之前的 statethis.memoizedState = null;// 节点更新过期时间,用于时间分片// react 17 改为:lanes、childLanesthis.expirationTime = NoLanesthis.childExpirationTime = NoLanes// 对应到页面的真实 DOM 节点this.stateNode = null// Fiber 节点的副本,可以理解为备胎,主要用于提升更新的性能this.alternate = null// 副作用相关,用于标记节点是否需要更新// 以及更新的类型:替换成新节点、更新属性、更新文本、删除……this.effectTag = NoEffect// 指向下一个需要更新的节点this.nextEffect = nullthis.firstEffect = nullthis.lastEffect = null}function commitRootImpl(root, renderPriorityLevel) {do {// `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which// means `flushPassiveEffects` will sometimes result in additional// passive effects. So we need to keep flushing in a loop until there are// no more pending effects.// TODO: Might be better if `flushPassiveEffects` did not automatically// flush synchronous work at the end, to avoid factoring hazards like this.flushPassiveEffects();} while (rootWithPendingPassiveEffects !== null);flushRenderPhaseStrictModeWarningsInDEV();invariant((executionContext & (RenderContext | CommitContext)) === NoContext,'Should not already be working.',);const finishedWork = root.finishedWork;const lanes = root.finishedLanes;if (__DEV__) {if (enableDebugTracing) {logCommitStarted(lanes);}}if (enableSchedulingProfiler) {markCommitStarted(lanes);}if (finishedWork === null) {if (__DEV__) {if (enableDebugTracing) {logCommitStopped();}}if (enableSchedulingProfiler) {markCommitStopped();}return null;} else {if (__DEV__) {if (lanes === NoLanes) {console.error('root.finishedLanes should not be empty during a commit. This is a ' +'bug in React.',);}}}root.finishedWork = null;root.finishedLanes = NoLanes;invariant(finishedWork !== root.current,'Cannot commit the same tree as before. This error is likely caused by ' +'a bug in React. Please file an issue.',);// commitRoot never returns a continuation; it always finishes synchronously.// So we can clear these now to allow a new callback to be scheduled.root.callbackNode = null;root.callbackPriority = NoLane;// Update the first and last pending times on this root. The new first// pending time is whatever is left on the root fiber.let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);markRootFinished(root, remainingLanes);if (root === workInProgressRoot) {// We can reset these now that they are finished.workInProgressRoot = null;workInProgress = null;workInProgressRootRenderLanes = NoLanes;} else {// This indicates that the last root we worked on is not the same one that// we're committing now. This most commonly happens when a suspended root// times out.}// If there are pending passive effects, schedule a callback to process them.// Do this as early as possible, so it is queued before anything else that// might get scheduled in the commit phase. (See #16714.)// TODO: Delete all other places that schedule the passive effect callback// They're redundant.if ((finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||(finishedWork.flags & PassiveMask) !== NoFlags) {if (!rootDoesHavePassiveEffects) {rootDoesHavePassiveEffects = true;scheduleCallback(NormalSchedulerPriority, () => {flushPassiveEffects();return null;});}}// Check if there are any effects in the whole tree.// TODO: This is left over from the effect list implementation, where we had// to check for the existence of `firstEffect` to satisfy Flow. I think the// only other reason this optimization exists is because it affects profiling.// Reconsider whether this is necessary.const subtreeHasEffects =(finishedWork.subtreeFlags &(BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==NoFlags;const rootHasEffect =(finishedWork.flags &(BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==NoFlags;if (subtreeHasEffects || rootHasEffect) {const prevTransition = ReactCurrentBatchConfig.transition;ReactCurrentBatchConfig.transition = 0;const previousPriority = getCurrentUpdatePriority();setCurrentUpdatePriority(DiscreteEventPriority);const prevExecutionContext = executionContext;executionContext |= CommitContext;// Reset this to null before calling lifecyclesReactCurrentOwner.current = null;// The commit phase is broken into several sub-phases. We do a separate pass// of the effect list for each phase: all mutation effects come before all// layout effects, and so on.// The first phase a "before mutation" phase. We use this phase to read the// state of the host tree right before we mutate it. This is where// getSnapshotBeforeUpdate is called.const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(root,finishedWork,);if (enableProfilerTimer) {// Mark the current commit time to be shared by all Profilers in this// batch. This enables them to be grouped later.recordCommitTime();}if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {// Track the root here, rather than in commitLayoutEffects(), because of ref setters.// Updates scheduled during ref detachment should also be flagged.rootCommittingMutationOrLayoutEffects = root;}// The next phase is the mutation phase, where we mutate the host tree.commitMutationEffects(root, finishedWork, lanes);if (enableCreateEventHandleAPI) {if (shouldFireAfterActiveInstanceBlur) {afterActiveInstanceBlur();}}resetAfterCommit(root.containerInfo);// The work-in-progress tree is now the current tree. This must come after// the mutation phase, so that the previous tree is still current during// componentWillUnmount, but before the layout phase, so that the finished// work is current during componentDidMount/Update.root.current = finishedWork;// The next phase is the layout phase, where we call effects that read// the host tree after it's been mutated. The idiomatic use case for this is// layout, but class component lifecycles also fire here for legacy reasons.if (__DEV__) {if (enableDebugTracing) {logLayoutEffectsStarted(lanes);}}if (enableSchedulingProfiler) {markLayoutEffectsStarted(lanes);}commitLayoutEffects(finishedWork, root, lanes);if (__DEV__) {if (enableDebugTracing) {logLayoutEffectsStopped();}}if (enableSchedulingProfiler) {markLayoutEffectsStopped();}if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {rootCommittingMutationOrLayoutEffects = null;}// Tell Scheduler to yield at the end of the frame, so the browser has an// opportunity to paint.requestPaint();executionContext = prevExecutionContext;// Reset the priority to the previous non-sync value.setCurrentUpdatePriority(previousPriority);ReactCurrentBatchConfig.transition = prevTransition;} else {// No effects.root.current = finishedWork;// Measure these anyway so the flamegraph explicitly shows that there were// no effects.// TODO: Maybe there's a better way to report this.if (enableProfilerTimer) {recordCommitTime();}}const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;if (rootDoesHavePassiveEffects) {// This commit has passive effects. Stash a reference to them. But don't// schedule a callback until after flushing layout work.rootDoesHavePassiveEffects = false;rootWithPendingPassiveEffects = root;pendingPassiveEffectsLanes = lanes;}// Read this again, since an effect might have updated itremainingLanes = root.pendingLanes;// Check if there's remaining work on this rootif (remainingLanes === NoLanes) {// If there's no remaining work, we can clear the set of already failed// error boundaries.legacyErrorBoundariesThatAlreadyFailed = null;}if (__DEV__ && enableStrictEffects) {if (!rootDidHavePassiveEffects) {commitDoubleInvokeEffectsInDEV(root.current, false);}}if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {markNestedUpdateScheduled();}// Count the number of times the root synchronously re-renders without// finishing. If there are too many, it indicates an infinite update loop.if (root === rootWithNestedUpdates) {nestedUpdateCount++;} else {nestedUpdateCount = 0;rootWithNestedUpdates = root;}} else {nestedUpdateCount = 0;}onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);if (enableUpdaterTracking) {if (isDevToolsPresent) {root.memoizedUpdaters.clear();}}if (__DEV__) {onCommitRootTestSelector();}// Always call this before exiting `commitRoot`, to ensure that any// additional work on this root is scheduled.ensureRootIsScheduled(root, now());if (hasUncaughtError) {hasUncaughtError = false;const error = firstUncaughtError;firstUncaughtError = null;throw error;}// If the passive effects are the result of a discrete render, flush them// synchronously at the end of the current task so that the result is// immediately observable. Otherwise, we assume that they are not// order-dependent and do not need to be observed by external systems, so we// can wait until after paint.// TODO: We can optimize this by not scheduling the callback earlier. Since we// currently schedule the callback in multiple places, will wait until those// are consolidated.if (includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&root.tag !== LegacyRoot) {flushPassiveEffects();}// If layout work was scheduled, flush it now.flushSyncCallbacks();if (__DEV__) {if (enableDebugTracing) {logCommitStopped();}}if (enableSchedulingProfiler) {markCommitStopped();}return null;}

