



















render阶段开始于performSyncWorkOnRoot或者performConcurrentWorkOnRoot.// performSyncWorkOnRoot会调用该方法
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
// performConcurrentWorkOnRoot会调用该方法
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
workInProcess fiberFiber节点, 并赋值给workInProcessworkInProcess与已经创建的Fiber节点链接起来, 构成一个fiber树Fiber Reconciler重构了Stack Reconciler, 通过遍历实现可中断的递归rootFiber开始向下深度优先遍历. 为每个fiber节点调用beginWork方法Fiber节点创建子Fiber节点, 并将两个Fiber节点链接起来.completeWork阶段, 处理fiber节点completeWork
兄弟Fiber节点, 会进入兄弟节点的递阶段兄弟fiber节点, 会进入父级Fiber的归阶段rootFiber. render阶段的工作完成单一文本的叶子节点不会执行
beginWork/completeWork
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
// ...省略函数体
}
current: 当前组件对应的Fiber节点在上一次更新时的Fiber节点. workInProcess.alternate
workInProcess: 当前组件对应的fiber节点
renderLanes: 优先级相关
区分mount还是update
current === nullcurrent !== nullbegin工作分为两部分:
current节点存在, 满足一定条件, 进行复用. 克隆current.child到workInProcess.childfiberRootNode以外, current === null. 根据fiber.tag不同, 创建不同类型的子fiber节点Update: 满足如下情况. -> didReceiveUpdate = false;直接复用上一次的fiber节点
oldProps === newProps && workInProcess.type === current.type不变includesSomeLane(renderLanes, updateLanes), 当前节点的优先级不够Mount:
Fiber的创建逻辑reconcileChildren: 根据fiber节点创建子fiber节点function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes
): Fiber | null {
// update时:如果current存在可能存在优化路径,可以复用current(即上一次更新的Fiber节点)
if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (
oldProps !== newProps ||
hasLegacyContextChanged() ||
(__DEV__ ? workInProgress.type !== current.type : false)
) {
didReceiveUpdate = true;
} else if (!includesSomeLane(renderLanes, updateLanes)) {
didReceiveUpdate = false;
switch (workInProgress.tag) {
// 省略处理
}
// 复用current
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderLanes,
);
} else {
didReceiveUpdate = false;
}
} else {
didReceiveUpdate = false;
}
// mount时:根据tag不同,创建不同的子Fiber节点
switch (workInProgress.tag) {
case IndeterminateComponent:
// ...省略
case LazyComponent:
// ...省略
case FunctionComponent:
// ...省略
case ClassComponent:
// ...省略
case HostRoot:
// ...省略
case HostComponent:
// ...省略
case HostText:
// ...省略
// ...省略其他类型
}
}
mount: 创建新的子fiber节点update: 会将当前fiber节点和上次更新的fiber节点对比(diff), 产生结果比较新的fiber节点子fiber节点, 赋值给workInProcess.child, 作为本次beginWork的返回值performUnitOfWork执行时workInProcess的传参reconcileChildFibers会为生成的Fiber节点带上effectTag属性export function reconcileChildren(
current: Fiber | null,
workInProgress: Fiber,
nextChildren: any,
renderLanes: Lanes
) {
if (current === null) {
// 对于mount的组件
workInProgress.child = mountChildFibers(
workInProgress,
null,
nextChildren,
renderLanes,
);
} else {
// 对于update的组件
workInProgress.child = reconcileChildFibers(
workInProgress,
current.child,
nextChildren,
renderLanes,
);
}
}
Renderer进行dom操作effectTag中Renderer将Fiber节点对应的dom插入到页面中需要满足两个条件
fiber.stateNode存在, 即fiber节点保存了对应的dom节点Fiber节点上存在Placement effectTagfiber.stateNode会在completeWork中完成rootFiber会被赋值Placement effectTag. commit阶段一次性插入完成
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
case IndeterminateComponent:
case LazyComponent:
case SimpleMemoComponent:
case FunctionComponent:
case ForwardRef:
case Fragment:
case Mode:
case Profiler:
case ContextConsumer:
case MemoComponent:
return null;
case ClassComponent: {
// ...省略
return null;
}
case HostRoot: {
// ...省略
updateHostContainer(workInProgress);
return null;
}
case HostComponent: {
// ...省略
return null;
}
// ...省略
fiber节点Fiber节点对应的dom节点已经存在了, 不需要生成dom节点, 主要处理props
onClick, onChange等回调函数的注册style propDANGEROUSLY_SET_INNER_HTML propchildren propupdateHostComponent
props赋值给workInProcess.updateQueuecommit阶段(Renderer)渲染在页面上workInProgress.updateQueue = (updatePayload: any);updatePayload为数组, 偶数为变化的prop key, 奇数为prop valuefiber节点增加对应的dom节点updateHostComponent类似的props处理过程appendAllChildren时, 都会将已生成的子孙dom节点插入到当前生成的dom节点下.rootFiber时, 已经有一颗已经构建好的内存中的dom树case HostComponent: {
popHostContext(workInProgress);
const rootContainerInstance = getRootHostContainer();
const type = workInProgress.type;
if (current !== null && workInProgress.stateNode != null) {
// update的情况
updateHostComponent(
current,
workInProgress,
type,
newProps,
rootContainerInstance,
);
} else {
// mount的情况
// ...省略服务端渲染相关逻辑
const currentHostContext = getHostContext();
// 为fiber创建对应DOM节点
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
// 将子孙DOM节点插入刚生成的DOM节点中
appendAllChildren(instance, workInProgress, false, false);
// DOM节点赋值给fiber.stateNode
workInProgress.stateNode = instance;
// 与update逻辑中的updateHostComponent类似的处理props的过程
if (
finalizeInitialChildren(
instance,
type,
newProps,
rootContainerInstance,
currentHostContext,
)
) {
markUpdate(workInProgress);
}
}
return null;
}
fiber节点, 找到标记effectTag的fibercompleteWork的上层函数completeUnitOfWork中:completeWork, 且存在effectTag的fiber节点, 会保存在effectList单向链表中fiber.firstEffectfiber.lastEffecteffectTag的Fiber节点, 都会追加到这条链表中rootFiber.firstEffect为起点的effectListcommit阶段只需要遍历effectList即可 nextEffect nextEffect
rootFiber.firstEffect -----------> fiber -----------> fiber
render阶段全部完成工作后.
performSyncWorkRoot函数中, fiberNode传递给commitRoot.commit阶段的工作
fiberRootNode作为传参fiberRootNode.firstEffect上保存了一条需要执行副作用的Fiber节点单向链表updateQueue上保存了变化的propscommit阶段执行commit阶段执行.commit阶段的主要工作
before mutation阶段: 执行dom操作前mutation阶段: 执行dom操作layout阶段: 执行dom操作后before mutation之前和layout之后还有一些工作
useEffect的触发优先级相关重置ref的绑定的解绑before mutation之前
firstEffect, commit三个子阶段都会用到layout之后
useEffect相关处理commit出发一些生命周期函数, hookeffectList并调用commitBeforeMutationEffects函数处理// 保存之前的优先级,以同步优先级执行,执行完毕后恢复之前优先级
const previousLanePriority = getCurrentUpdateLanePriority();
setCurrentUpdateLanePriority(SyncLanePriority);
// 将当前上下文标记为CommitContext,作为commit阶段的标志
const prevExecutionContext = executionContext;
executionContext |= CommitContext;
// 处理focus状态
focusedInstanceHandle = prepareForCommit(root.containerInfo);
shouldFireAfterActiveInstanceBlur = false;
// beforeMutation阶段的主函数
commitBeforeMutationEffects(finishedWork);
focusedInstanceHandle = null;
commitBeforeMutationEffects
DOM节点渲染/删除后的autoFocus, blur的逻辑getSnapshotBeforeUpdate生命周期钩子useEffectfunction commitBeforeMutationEffects() {
while (nextEffect !== null) {
const current = nextEffect.alternate;
if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
// ...focus blur相关
}
const effectTag = nextEffect.effectTag;
// 调用getSnapshotBeforeUpdate
if ((effectTag & Snapshot) !== NoEffect) {
commitBeforeMutationEffectOnFiber(current, nextEffect);
}
// 调度useEffect
if ((effectTag & Passive) !== NoEffect) {
if (!rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = true;
// 以某个优先级调用一个回调函数
scheduleCallback(NormalSchedulerPriority, () => {
// 调用useEffect
flushPassiveEffects();
return null;
});
}
}
nextEffect = nextEffect.nextEffect;
}
}
getSnapshotBeforeUpdate
render阶段的任务可能被中断重新开始, componentWillXXX钩子可能被多次触发. 变为不安全的getSnapshotBeforeUpdate, 在commit阶段内同步执行, 所以只会触发一次flushPassiveEffects方法从全局变量rootWithPendingPassiveEffects获取effectListeffectList包含了需要执行副作用的Fiber节点
FunctionComponent含有useEffect或者useLayoutEffect时, 它的fiber节点也会含有Passive effectTagflushPassiveEffect内部遍历effectList, 执行effect回调函数useEffect分成三步:
before mutation阶段在scheduleCallback中调度flushPassiveEffectslayout阶段之后, 将effectList赋值给rootWidthPendingPassiveEffectsscheduleCallBack触发flushPassiveEffects, flushPassiveEffects内部遍历rootWidthPendingPassiveEffectsuseEffect的函数会延迟调用.effectList, 执行函数, commitMutationEffectsnextEffect = firstEffect;
do {
try {
commitMutationEffects(root, renderPriorityLevel);
} catch (error) {
invariant(nextEffect !== null, 'Should be working on an effect.');
captureCommitPhaseError(nextEffect, error);
nextEffect = nextEffect.nextEffect;
}
} while (nextEffect !== null);
commitMutationEffects
ContentRest effectTag重置文本节点refeffectTag分别处理, 其中effectTag分别包括(Placement|Update|Deletion|Hydrating)function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {
// 遍历effectList
while (nextEffect !== null) {
const effectTag = nextEffect.effectTag;
// 根据 ContentReset effectTag重置文字节点
if (effectTag & ContentReset) {
commitResetTextContent(nextEffect);
}
// 更新ref
if (effectTag & Ref) {
const current = nextEffect.alternate;
if (current !== null) {
commitDetachRef(current);
}
}
// 根据 effectTag 分别处理
const primaryEffectTag = effectTag & (Placement | Update | Deletion | Hydrating);
switch (primaryEffectTag) {
// 插入DOM
case Placement: {
commitPlacement(nextEffect);
nextEffect.effectTag &= ~Placement;
break;
}
// 插入DOM 并 更新DOM
case PlacementAndUpdate: {
// 插入
commitPlacement(nextEffect);
nextEffect.effectTag &= ~Placement;
// 更新
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// SSR
case Hydrating: {
nextEffect.effectTag &= ~Hydrating;
break;
}
// SSR
case HydratingAndUpdate: {
nextEffect.effectTag &= ~Hydrating;
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// 更新DOM
case Update: {
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// 删除DOM
case Deletion: {
commitDeletion(root, nextEffect, renderPriorityLevel);
break;
}
}
nextEffect = nextEffect.nextEffect;
}
}
Placement effect: fiber节点对应的dom节点需要插入到页面中
getHostSibling非常耗时, 因为同一个fiber节点不止包含HostComponent*parentNode.insertBefore或者parentNode.appendChildUpdate effect: fiber节点需要更新, 调用commitWork
FunctionComponent mutation
fiber.tag为FunctionComponent, 会调用commitHookEffectListUnmount.effectList, 执行所有useLayoutEffect hook的销毁函数HostComponent mutation
commitUpdateupdateDOMProperties中将render阶段 completeWork中为fiber节点赋值的updateQueue对应的内容, 渲染在页面上for (let i = 0; i < updatePayload.length; i += 2) {
const propKey = updatePayload[i];
const propValue = updatePayload[i + 1];
// 处理 style
if (propKey === STYLE) {
setValueForStyles(domElement, propValue);
// 处理 DANGEROUSLY_SET_INNER_HTML
} else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
setInnerHTML(domElement, propValue);
// 处理 children
} else if (propKey === CHILDREN) {
setTextContent(domElement, propValue);
} else {
// 处理剩余 props
setValueForProperty(domElement, propKey, propValue, isCustomComponentTag);
}
}
fiber节点含有effectTag时, 调用commitDeletion
fiber节点以及子孙fiber节点. 调用fiber.tag为ClassComponent的componentWillUnComponent.fiber节点对应的dom节点refuseEffect的销毁函数hook可以直接访问到已经改变后的domeffectList, 执行函数function commitLayoutEffects(root: FiberRoot, committedLanes: Lanes) {
while (nextEffect !== null) {
const effectTag = nextEffect.effectTag;
// 调用生命周期钩子和hook
if (effectTag & (Update | Callback)) {
const current = nextEffect.alternate;
commitLayoutEffectOnFiber(root, current, nextEffect, committedLanes);
}
// 赋值ref
if (effectTag & Ref) {
commitAttachRef(nextEffect);
}
nextEffect = nextEffect.nextEffect;
}
}
commitLayoutEffects
commitLayoutEffectOnFiber调用生命周期钩子和hook相关操作
ClassComponent调用ComponentDidMount/ComponentDidUpdatethis.state()第二个参数函数FunctionComponent调用useLayoutEffect回调函数, 调度useEffect的销毁和回调函数useLayoutEffect(): 从上一次更新的销毁到这次更新执行, 是同步执行的useEffect(): 需要先调度, 在Layout阶段执行完成后, 再异步执行commitAttachRef: 赋值ref: 获取dom实例, 更新dommutation阶段执行后, layout开始前, 切换root.current = finishWork
因为layout执行的生命周期函数和hook需要获取新的dom信息
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。