-
Notifications
You must be signed in to change notification settings - Fork 4
Description
简要的更新过程
以 FunctionComponent 和 一次 click 内 setState 为例
setState
省略 React 处理事件过程,直接进入 onClick 处理函数调用 setState,这里的 setState 就是 dispatchSetState 函数。
- requestUpdateLane 根据触发事件的类型获取优先级。click 为 1,是离散事件的优先级(最开始的时候默认为 16,如果是 transition 则从 64 开始)
- 构造 update 数据结构
- hasEagerState 和 eagerState 是优化策略。当首次更新(首次更新判断条件:
fiber.lanes === NoLane && (!fiber.alternate || fiber.alternate.lanes === NoLane),则会被设置为 true,并计算出 state 记录。如果计算的 state 和当前 state 一致,则直接退出本次更新
- hasEagerState 和 eagerState 是优化策略。当首次更新(首次更新判断条件:
- enqueueUpdate (存储到 hook.queue)
- 调用 scheduleUpdateOnFiber 进入更新调度
scheduleUpdateOnFiber
- markUpdateLaneFromFiberToRoot 在 sourceFiber 和 alternate 上都设置 lanes。并通过 childLanes 向上冒泡设置。最后返回 root(HostRoot)
- markRootUpdated 也就是设置 root.pendingLanes 和 eventTimes。这里的 pendingLanes 会主要在 getNextLanes 里面使用到
if 分支处理
- 同(root === workInProgressRoot)+渲染阶段,触发更新的处理。那么就标记 workInProgressRootRenderPhaseUpdatedLanes
- 反之
- root === workInProgressRoot 已经是当前更新
- 情况一:deferRenderPhaseUpdateToNextBatch
- 情况二:workInProgressRootExitStatus === RootSuspendedWithDelay 设置 root.suspendedLanes
- ensureRootIsScheduled
- 👆函数正常退出后,针对旧版的兼容处理,setTimeout 等这类内触发的更新,需要同步调用,也就是立即出发 React 渲染工作 performSyncWorkOnRoot
- root === workInProgressRoot 已经是当前更新
ensureRootIsScheduled
以 Root 为参数,安排调度 performSyncWorkOnRoot
- markStarvedLanesAsExpired 处理是否有过期的更新(开启并发之后,防止低优先级的任务始终没有机会执行的问题)。从 pendingLanes 这个赛道里面分理出过期的任务,放到 expiredLanes 赛道内
- getNextLanes 获取当前执行中的优先级
- pendingLanes 是首先判断的条件。如果这个为 NoLanes,那么就直接返回 NoLanes
if 分支处理:没有获取到调度优先级
-
nextLanes === NoLanes 说明没有需要做的更新,重置相关变量,退出更新
-
getHighestPriorityLane(nextLanes) 取出最高优先级的任务作为本次更新的任务调度优先级
if 分支处理:本次更新和当前更新(如果有)比较
- 和当前更新任务相同,直接退出更新
- 优先级不同,并且当前存在执行中的调度任务(并发嘛,可能存在执行中的任务),则取消当前任务
- 新建任务
- 同步:
scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root))- 如果支持
supportsMicrotasks,那么安排一下flushSyncCallbacks(这里面会执行 performSyncWorkOnRoot,也就是将 React 工作安排在同一个浏览器 task 里面) - 不支持,通过
scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks),这就会被安排到下一个 task 立即执行
- 如果支持
- 并发:
scheduleCallback(schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root))安排到下一个浏览器 task
- 同步:
performSyncWorkOnRoot
开始遍历 root 前的一些准备工作
-
flushPassiveEffects 如果存在 rootWithPendingPassiveEffects(这个会在 commitRoot 阶段被设置。如果存在说明上一次任务执行完,调度的 effect 还没有执行。那么就需要在本次更新任务执行前,执行完成。留给本次更新一个干净的环境),则执行
- commitPassiveUnmountEffects
- 处理删除节点的 destroy,方向是 parent -> child。detachFiberAfterEffects 方向是 child -> parent
- 处理本次在更新 HookHasEffect 范围内的 destroy,方向是从 child -> parent
- commitPassiveMountEffects
- 处理本次在更新 HookHasEffect 范围内的 create,方向是从 child -> parent
- commitPassiveUnmountEffects
-
getNextLanes 再次获取当前任务的优先级
if 分支判断:优先级 -
不包含 SyncLane,则将本次更新调用 ensureRootIsScheduled 重新安排,并退出本次更新,等重新调度
-
包含 SyncLane,调用 renderRootSync 开始遍历
-
👆函数执行完成有一个退出码
- RootErrored 错误处理,重试一次
- RootFatalErrored 错误处理,直接清空当前栈帧(prepareFreshStack),处理 Suspense 标记(markRootSuspended)。然后安排调度任务(ensureRootIsScheduled)。最后抛出异常退出
- 设置如下状态,然后 commitRoot
const finishedWork: Fiber = (root.current.alternate: any);
root.finishedWork = finishedWork;
root.finishedLanes = lanes;- commitRoot 正常退出之后,再次安排调度 ensureRootIsScheduled
React 核心工作:Render + Commit
Render 阶段
Commit 阶段
- 重置当前任务的一些状态
root.callbackNode = null;
root.callbackPriority = NoLane;- 处理已经完成的赛道(任务)
let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
markRootFinished(root, remainingLanes);- 其他状态
workInProgressRoot = null;
workInProgress = null;
workInProgressRootRenderLanes = NoLanes;- 是否有副作用,有的话安排调度 useEffect 等。👇会判断是否有同步,如果有那么就会同步执行 useEffect
if (
(finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||
(finishedWork.flags & PassiveMask) !== NoFlags
) {
if (!rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = true;
pendingPassiveEffectsRemainingLanes = remainingLanes;
// workInProgressTransitions might be overwritten, so we want
// to store it in pendingPassiveTransitions until they get processed
// We need to pass this through as an argument to commitRoot
// because workInProgressTransitions might have changed between
// the previous render and commit if we throttle the commit
// with setTimeout
pendingPassiveTransitions = transitions;
scheduleCallback(NormalSchedulerPriority, () => {
flushPassiveEffects();
// This render triggered passive effects: release the root cache pool
// *after* passive effects fire to avoid freeing a cache pool that may
// be referenced by a node in the tree (HostRoot, Cache boundary etc)
return null;
});
}
}
-
如果有内容更新,比如 DOM 变化等。首先设置执行状态
executionContext |= CommitContext。然后是 3 大步骤: -
commitBeforeMutationEffects
- 焦点事件等一些处理
- 这一阶段,FunctionComponent 没有事做。不过可以提一下 ClassComponent,会调用
getSnapshotBeforeUpdate,方向是 child -> parent
-
commitMutationEffects
- 👇都是删除的处理,方向是 parent -> child
- 如果 FunctionComponent 有删除的状态(parentFiber.deletions),调用 destroy 函数(HookInsertion, HookLayout),删除同时会将 fiber 从树内删除(detachFiberMutation)
- 补充:ClassComponent safelyDetachRef 和 componentWillUnmount,方向是 parent -> child
- 补充:HostComponent safelyDetachRef。方向是 parent -> child (commitDeletionEffectsOnFiber 里面,有一个很故意的做法,
case HostComponent时没有break也没有return,于是就会进入👇 HostText 的处理) - 补充:HostText removeChild。方向是 parent -> child
- commitPlacement 插入 DOM (insertOrAppendPlacementNode),遍历方向是 child -> parent。DOM 节点的插入方向 parent -> child
- 下面是更新的处理
- 有更新(Update)处理:commitHookEffectListUnmount(HookInsertion | HookHasEffect),commitHookEffectListMount(HookInsertion | HookHasEffect), commitHookEffectListUnmount(HookLayout | HookHasEffect) 即 useLayoutEffect 的 destroy 在这里被执行
- 补充:ClassComponent safelyDetachRef,方向是 child -> parent
- 补充:HostComponent safelyDetachRef,如果有 Update,commitUpdate 。方向是 child -> parent
-
commitLayoutEffects
- commitHookEffectListMount(HookLayout | HookHasEffect) 即 useLayoutEffect 的 create 在这里被执行。方向是 child -> parent
- 补充:ClassComponent componentDidUpdate(首次就是 componentDidMount) 和 setState 的 callback
- 补充:HostComponent 会处理 autoFocus
-
设置 useEffect 执行需要的状态
rootDoesHavePassiveEffects = false;
// 👇这个是遍历时需要的数据
rootWithPendingPassiveEffects = root;
pendingPassiveEffectsLanes = lanes;-
再次安排调度 ensureRootIsScheduled,确定没有需要更新的内容
-
如果本次任务有同步的优先级,那么同步执行 useEffect(针对的是离散型事件,比如点击,让其同步执行)
if (
includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&
root.tag !== LegacyRoot
) {
flushPassiveEffects();
}- 处理剩余的任务 remainingLanes
- 循环调用检查,就是那个 50 次
- 调用 flushSyncCallbacks,确保同步任务都被执行完成(If layout work was scheduled, flush it now.)
- 结束
此时使用 updateState,来到 updateReducer 处理函数