React Render阶段
目录
前言
本文是React源码学习系列第三篇,该系列整体都基于React18.0.0版本源码。旨在学习过程中记录一些个人的理解。该篇介绍React Render过程。
入口
React调度最终都是执行的performSyncWorkOnRoot、performConcurrentWorkOnRoot方法。 本文以performConcurrentWorkOnRoot展开讲解。 并发模式下需要先检查上一次更新有没有Effect还没执行完,有的话执行他。然后判断当前任务有没有被更换,因为Effect有可能产生新的更高优先级任务。
function performConcurrentWorkOnRoot(root, didTimeout) {
currentEventTime = NoTimestamp;
currentEventTransitionLane = NoLanes;
const originalCallbackNode = root.callbackNode;
// 确保上一个更新所有的Effect都已经执行
const didFlushPassiveEffects = flushPassiveEffects();
// 如果上一个更新有Effect,需要检查任务是否有变更。因为Effect有可能产生新的高优先级任务,打断当前更新任务。
if (didFlushPassiveEffects) {
// 任务变更了
if (root.callbackNode !== originalCallbackNode) {
// 直接返回null,不需要重新调度,因为更高优先级的任务在ensureRootIsScheduled方法里面已经发起了一个调度。
return null;
}
}
// 在上一篇里面讲过了。
let lanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
);
// 没有待更新的leans 退出调度
if (lanes === NoLanes) {
return null;
}
// 是否开启时间切片 满足以下几个条件开启
// 1.不包含阻塞lane
// 2.lanes没有过期lane
// 3.lanes是否包含在root.expiredLanes内。
// 4.该任务没有过期
const shouldTimeSlice =
!includesBlockingLane(root, lanes) &&
!includesExpiredLane(root, lanes) &&
(disableSchedulerTimeoutInWorkLoop || !didTimeout);
// 执行同步或者并发
let exitStatus = shouldTimeSlice
? renderRootConcurrent(root, lanes)
: renderRootSync(root, lanes);
if (exitStatus !== RootInProgress) {
if (exitStatus === RootErrored) {
// 忽略错误
}
if (exitStatus === RootFatalErrored) {
// 忽略错误
}
if (exitStatus === RootDidNotComplete) {
// 忽略未完成
} else {
// 到这里说明已经完成了Render阶段
const renderWasConcurrent = !includesBlockingLane(root, lanes);
const finishedWork: Fiber = (root.current.alternate:any);
// 到这里已经拥有了一棵完整的 workInProgress Fiber tree。
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
// 进入提交阶段
finishConcurrentRender(root, exitStatus, lanes);
}
}
// 工作未完成,退出之前,确保为下一个pending发起调度。
ensureRootIsScheduled(root, now());
if (root.callbackNode === originalCallbackNode) {
// 说明是同一个任务因为时间切片耗尽而暂停,返回继续参与调度
return performConcurrentWorkOnRoot.bind(null, root);
}
return null;
}
执行renderRootConcurrent、renderRootSync方法。本文以renderRootConcurrent展开讲解。
保留核心代码
function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
// 注入Dispatcher
const prevDispatcher = pushDispatcher();
// 如果root或lanes发生了变化,说明有更高优先级的更新任务打断了当前更新,重新准备一个新栈。
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
// 准备一个新的stack
prepareFreshStack(root, lanes);
}
do {
try {
workLoopConcurrent();
// 内部被中断会执行break;跳出循环。
break;
} catch (thrownValue) {
handleError(root, thrownValue);
}
} while (true);
popDispatcher(prevDispatcher);
// 走到这里说明Render被中断、或者完成了
if (workInProgress !== null) {
// 返回进行中状态码
return RootInProgress;
} else {
// 完成工作,重置状态
workInProgressRoot = null;
workInProgressRootRenderLanes = NoLanes;
// 返回完成状态码
return workInProgressRootExitStatus;
}
}
准备环境
prepareFreshStack方法主要准备一个全新的。
- 重置状态。
- workInProgress设为HostRootFiber。每次Render都是从HostRootFiber按照深度优先往下再往上回到HostRootFiber的一个过程。
- 这里要注意一点,rootWorkInProgress创建的时候是根据HostRootFiber.current(current HostRootFiber)创建的,初次渲染的时候只有它是有alternate属性。这是初次渲染过程中往宿主只真实的插入一次DOM的关键原因。
// 保留关键代码
function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {
root.finishedWork = null;
root.finishedLanes = NoLanes;
workInProgressRoot = root;
// HostRootFiber 从创建的时候就有alternate,所以mount的时候也只有HostRootFiber的child Fiber会打上Placement标记,从而只操作一次dom
const rootWorkInProgress = createWorkInProgress(root.current, null);
// workInProgress最开始为HostRootFiber。
workInProgress = rootWorkInProgress;
// 本次要更新的lanes
workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;
// Render状态
workInProgressRootExitStatus = RootInProgress;
// 从挂起恢复的lanes
workInProgressRootPingedLanes = NoLanes;
return rootWorkInProgress;
}
createWorkInProgress方法。
// 保留核心代码
function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
// HostRootFiber.current在创建root的时候就已经有了。
let workInProgress = current.alternate;
// mount 就新建一个Fiber
if (workInProgress === null) {
workInProgress = createFiber(
current.tag,
pendingProps,
current.key,
current.mode,
);
workInProgress.elementType = current.elementType;
workInProgress.type = current.type;
workInProgress.stateNode = current.stateNode;
workInProgress.alternate = current;
current.alternate = workInProgress;
} else {
// 复用current
workInProgress.pendingProps = pendingProps;
workInProgress.type = current.type;
workInProgress.flags = NoFlags;
workInProgress.subtreeFlags = NoFlags;
workInProgress.deletions = null;
}
// 复用属性
workInProgress.flags = current.flags & StaticMask;
workInProgress.childLanes = current.childLanes;
workInProgress.lanes = current.lanes;
workInProgress.child = current.child;
workInProgress.memoizedProps = current.memoizedProps;
workInProgress.memoizedState = current.memoizedState;
workInProgress.updateQueue = current.updateQueue;
const currentDependencies = current.dependencies;
workInProgress.dependencies =
currentDependencies === null
? null
: {
lanes: currentDependencies.lanes,
firstContext: currentDependencies.firstContext,
};
workInProgress.sibling = current.sibling;
workInProgress.index = current.index;
workInProgress.ref = current.ref;
return workInProgress;
}
workLoopConcurrent方法会判断单帧时间是否用尽,用尽的话会中断Render流程,交出主进程,等待下一帧的空闲时间继续调度。
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
Render
performUnitOfWork方法。从HostRootFiber开始向下以深度优先模式开始构建workInProgress Fiber tree。 beginWork和completeWork会交替执行,直到到达HostRootFiber,然后进入提交阶段。
先在beginWork阶段自上而下收集各个Fiber节点在本次更新中产生的副作用,再借着completeWork向上冒泡的时机。把子孙节点的更新信息及副作用信息记录到父辈Fiber节点上。方便后面提交阶段做优化。
// 保留核心代码
function performUnitOfWork(unitOfWork: Fiber): void {
// 双缓存
const current = unitOfWork.alternate;
let next;// 用于移动workInProgress指针
next = beginWork(current, unitOfWork, subtreeRenderLanes);
// memoizedProps保存上一次的pendingProps。即oldProps
unitOfWork.memoizedProps = unitOfWork.pendingProps;
// beginWork返回null,进入归阶段。
if (next === null) {
completeUnitOfWork(unitOfWork);
} else {
// 继续执行beginWork
workInProgress = next;
}
}
beginWork
beginWork方法,为workInProgress创建子Fiber。 beginWork会以从上往下执行,直到到达叶子节点或者子节点不包含本次更新的lanes。然后执行completeWork方法。
- 这里会进行一次判断,是否命中优化策略。优化策略后面单独讲,本篇先跳过。
- 根据不同类型的组件做不同处理。
// 保留核心代码
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
// 初次渲染时候除了HostRootFiber,其他Fiber的current都为空
if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
// props是一个全新对的引用,所以就算前后两次props的每一个属性都没有变化也会判断不同。
// 只有当父fiberNode命中bailout策略,复用子fiberNode时,才会相等。
// HostRootFiber默认情况下满足oldProps === newProps。
if (oldProps !== newProps || hasLegacyContextChanged()){
didReceiveUpdate = true;
} else {
// props和legacy context都没有改变。检查是否有pending update or context change。
// 是否有更新 内部返回(current.lanes & renderLanes) !== NoLanes;
const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
current,
renderLanes,
);
if (
!hasScheduledUpdateOrContext &&
// DidCapture标记为suspense组件相关,先忽略,后面在讲解。
(workInProgress.flags & DidCapture) === NoFlags
) {
didReceiveUpdate = false;
// 命中优化策略, 复用之前的节点
return attemptEarlyBailoutIfNoScheduledUpdate(
current,
workInProgress,
renderLanes,
);
}
}
} else {
didReceiveUpdate = false;
}
workInProgress.lanes = NoLanes;
switch (workInProgress.tag) {
// FC组件第一次走这里
case IndeterminateComponent: {
return mountIndeterminateComponent(
current,
workInProgress,
workInProgress.type,
renderLanes,
);
}
// FC组件update走这里
case FunctionComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateFunctionComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
// HostRootFiber
case HostRoot:
return updateHostRoot(current, workInProgress, renderLanes);
// DOM类型Fiber
case HostComponent:
return updateHostComponent(current, workInProgress, renderLanes);
// 文本类型Fiber
case HostText:
return updateHostText(current, workInProgress);
// 忽略其他组件
}
}
updateHostRoot
HostRootFiber第一个进入节点。 updateHostRoot方法。
// 保留核心代码
function updateHostRoot(current, workInProgress, renderLanes) {
const nextProps = workInProgress.pendingProps;
const prevState = workInProgress.memoizedState;
const prevChildren = prevState.element;
// 从current克隆UpdateQueue。
cloneUpdateQueue(current, workInProgress);
processUpdateQueue(workInProgress, nextProps, null, renderLanes);
const nextState: RootState = workInProgress.memoizedState;
const root: FiberRoot = workInProgress.stateNode;
const nextChildren = nextState.element;
// HostRootFiber除了首次渲染会进入diff过程,其他时候正常都会命中优化策略。
if (nextChildren === prevChildren) {
// 优化策略
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
// 进入diff过程
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
processUpdateQueue方法。
// 保留核心代码
function processUpdateQueue<State>(
workInProgress: Fiber,
props: any,
instance: any,
renderLanes: Lanes,
): void {
// 这在ClassComponent或HostRoot中总是非空的
const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
let firstBaseUpdate = queue.firstBaseUpdate;
let lastBaseUpdate = queue.lastBaseUpdate;
// 检查是否有待处理的更新。跟baseQueue连接起来。
let pendingQueue = queue.shared.pending;
if (pendingQueue !== null) {
queue.shared.pending = null;
const lastPendingUpdate = pendingQueue;
const firstPendingUpdate = lastPendingUpdate.next;
lastPendingUpdate.next = null;
if (lastBaseUpdate === null) {
firstBaseUpdate = firstPendingUpdate;
} else {
lastBaseUpdate.next = firstPendingUpdate;
}
lastBaseUpdate = lastPendingUpdate;
const current = workInProgress.alternate;
if (current !== null) {
const currentQueue: UpdateQueue<State> = (current.updateQueue: any);
const currentLastBaseUpdate = currentQueue.lastBaseUpdate;
if (currentLastBaseUpdate !== lastBaseUpdate) {
if (currentLastBaseUpdate === null) {
currentQueue.firstBaseUpdate = firstPendingUpdate;
} else {
currentLastBaseUpdate.next = firstPendingUpdate;
}
currentQueue.lastBaseUpdate = lastPendingUpdate;
}
}
}
// 当我们处理队列时,这些值可能会改变。
if (firstBaseUpdate !== null) {
// 遍历更新列表以计算结果。
let newState = queue.baseState;
let newLanes = NoLanes;
let newBaseState = null;
let newFirstBaseUpdate = null;
let newLastBaseUpdate = null;
let update = firstBaseUpdate;
do {
const updateLane = update.lane;
const updateEventTime = update.eventTime;
// 该Update的lane不包含在当前调度的lanes里面 (优先级不够或者优先级低)
if (!isSubsetOfLanes(renderLanes, updateLane)) {
// 优先级不够。跳过此更新。如果这是第一次跳过的更新,那么之前的更新/状态就是新的基本更新/状态。
const clone: Update<State> = {
eventTime: updateEventTime,
lane: updateLane,
tag: update.tag,
payload: update.payload,
callback: update.callback,
next: null,
};
// 缓存因为优先级不够而没有参与计算的Update。
if (newLastBaseUpdate === null) {
newFirstBaseUpdate = newLastBaseUpdate = clone;
newBaseState = newState;
} else {
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
// 把该Update的lanes合并到newLanes
newLanes = mergeLanes(newLanes, updateLane);
} else { // 优先级足够
// 前面有Update因为优先级不足没执行,后面的Update都不执行,并把lane改为noLane
if (newLastBaseUpdate !== null) {
const clone: Update<State> = {
eventTime: updateEventTime,
// 确保下一次调度满足优先级足够 因为本来这一次都能执行了
lane: NoLane,
tag: update.tag,
payload: update.payload,
callback: update.callback,
next: null,
};
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
newState = getStateFromUpdate(
workInProgress,
queue,
update,
newState,
props,
instance,
);
const callback = update.callback;
if (
callback !== null &&
// 如果更新已经提交,我们不应该再次为它的回调排队。
update.lane !== NoLane
) {
// 有回调函数,打上标记。root.render()的第三个参数或者this.setState的第二个参数。
workInProgress.flags |= Callback;
const effects = queue.effects;
if (effects === null) {
queue.effects = [update];
} else {
effects.push(update);
}
}
}
update = update.next;
if (update === null) {
pendingQueue = queue.shared.pending;
// 所有Update都执行完毕,跳出循环。
if (pendingQueue === null) {
break;
} else {
// 产生了新的更新队列,继续执行。
const lastPendingUpdate = pendingQueue;
const firstPendingUpdate = ((lastPendingUpdate.next: any): Update<State>);
lastPendingUpdate.next = null;
update = firstPendingUpdate;
queue.lastBaseUpdate = lastPendingUpdate;
queue.shared.pending = null;
}
}
} while (true);
// 全部update执行完毕,没有因为优先级不足导致的没有执行的Update
if (newLastBaseUpdate === null) {
newBaseState = newState;
}
// 缓存中间State,因为有可能有Update因为优先级不足而未得到执行。
queue.baseState = ((newBaseState: any): State);
queue.firstBaseUpdate = newFirstBaseUpdate;
queue.lastBaseUpdate = newLastBaseUpdate;
// 剩余未执行的lanes
workInProgress.lanes = newLanes;
// 把计算出来的新state赋值给Fiber.memoizedState;
// 如果Update都执行完了Fiber.memoizedState === Fiber.updateQueue.baseState;
workInProgress.memoizedState = newState;
}
}
拼接baseQueue和shared.pending图例。
getStateFromUpdate方法。根据Update中的payload、prevState和nextProps计算出新的State。
- this.setState((prev) => prev);
- this.setState({xx:xx});
// 保留核心代码
function getStateFromUpdate<State>(
workInProgress: Fiber,
queue: UpdateQueue<State>,
update: Update<State>,
prevState: State,
nextProps: any,
instance: any,
): any {
switch (update.tag) {
case ReplaceState: {
const payload = update.payload;
if (typeof payload === 'function') {
const nextState = payload.call(instance, prevState, nextProps);
return nextState;
}
return payload;
}
case CaptureUpdate: {
// DidCapture标记为suspense组件相关,后面在讨论。
workInProgress.flags =
(workInProgress.flags & ~ShouldCapture) | DidCapture;
}
case UpdateState: {
const payload = update.payload;
let partialState;
if (typeof payload === 'function') {
partialState = payload.call(instance, prevState, nextProps);
} else {
partialState = payload;
}
if (partialState === null || partialState === undefined) {
return prevState;
}
return assign({}, prevState, partialState);
}
case ForceUpdate: {
// 强制更新直接返回老State
hasForceUpdate = true;
return prevState;
}
}
return prevState;
}
mountIndeterminateComponent
函数组件第一次会走这个方法。
// 保留核心代码
function mountIndeterminateComponent(
_current,
workInProgress,
Component,
renderLanes,
) {
if (_current !== null) {
_current.alternate = null;
workInProgress.alternate = null;
workInProgress.flags |= Placement;
}
const props = workInProgress.pendingProps;
let value;
let hasId;
value = renderWithHooks(
null,
workInProgress,
Component,
props,
context,
renderLanes,
);
if (!disableModulePatternComponents &&
typeof value === 'object' &&
value !== null &&
typeof value.render === 'function' &&
value.$$typeof === undefined
) {
// 忽略ClassComponente组件
} else { // 确定是FC
workInProgress.tag = FunctionComponent;
//进入Diff过程
reconcileChildren(null, workInProgress, value, renderLanes);
return workInProgress.child;
}
}
updateFunctionComponent
// 保留核心代码
function updateFunctionComponent(
current,
workInProgress,
Component,
nextProps: any,
renderLanes,
) {
let nextChildren;
let hasId;
nextChildren = renderWithHooks(
current,
workInProgress,
Component,
nextProps,
context,
renderLanes,
);
// 新旧props必须完全相等才可能命中(即父组件必须命中优化、子组件才有可能命中) 优化策略
if (current !== null && !didReceiveUpdate) {
bailoutHooks(current, workInProgress, renderLanes);
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
// 进入Diff过程
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
renderWithHooks,重置状态及执行函数,返回children。
// 保留核心代码
function renderWithHooks<Props, SecondArg>(
current: Fiber | null,
workInProgress: Fiber,
Component: (p: Props, arg: SecondArg) => any,
props: Props,
secondArg: SecondArg,
nextRenderLanes: Lanes,
): any {
renderLanes = nextRenderLanes;
currentlyRenderingFiber = workInProgress;
// 置空currentlyRenderingFiber里面的memoizedState(即hooks链表)、 updateQueue、lanes
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
workInProgress.lanes = NoLanes;
// 注入Dispatcher
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
// 执行函数组件
let children = Component(props, secondArg);
renderLanes = NoLanes;
currentlyRenderingFiber = (null: any);
currentHook = null;
workInProgressHook = null;
return children;
}
updateHostComponent
DOM类Fiber会进入该方法。
// 保留核心代码
function updateHostComponent(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
const type = workInProgress.type;
const nextProps = workInProgress.pendingProps;
const prevProps = current !== null ? current.memoizedProps : null;
let nextChildren = nextProps.children;
// 子节点是否只有文本
const isDirectTextChild = shouldSetTextContent(type, nextProps);
if (isDirectTextChild) {
// 特殊处理
nextChildren = null;
}
markRef(current, workInProgress);
// 进入diff过程
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
markRef,以下两种情况打上ref标记。
- mount阶段有ref
- update阶段ref有变更
// 保留核心代码
function markRef(current: Fiber | null, workInProgress: Fiber) {
const ref = workInProgress.ref;
if (
(current === null && ref !== null) ||
(current !== null && current.ref !== ref)
) {
// 打上ref标记
workInProgress.flags |= Ref;
}
}
updateHostText
// 保留核心代码
function updateHostText(current, workInProgress) {
// 文本节点没有子节点,开始归阶段(completWork)
return null;
}
DIFF过程
reconcileChildren,mountChildFibers和reconcileChildFibers指向同一个函数(reconcileChildFibers),唯一区别是是否追踪副作用,即是否需要打上各种副作用标记。如Placement。
Diff过程会根据不同类型的VDOM进行不同的DIFF过程,该过程比较重要,本篇不做详细讨论。会专门开一篇文章详细解说。概括来说就是通过前后对比,复用或者新建子节点,并返回第一个子节点,继续beginWork。
// 保留核心代码
const reconcileChildFibers = ChildReconciler(true);
const mountChildFibers = ChildReconciler(false);
export function reconcileChildren(
current: Fiber | null,
workInProgress: Fiber,
nextChildren: any,
renderLanes: Lanes,
) {
// 初次渲染除了HostRootFiber其它都走这里
if (current === null) { // 不追踪副作用
workInProgress.child = mountChildFibers(
workInProgress,
null,
nextChildren,
renderLanes,
);
} else { // 追踪副作用
workInProgress.child = reconcileChildFibers(
workInProgress,
current.child,
nextChildren,
renderLanes,
);
}
}
reconcileChildren方法。
// 保留关键代码
function ChildReconciler(shouldTrackSideEffects) {
// 根据不同类型 进入不同的diff流程
function reconcileChildFibers(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChild: any,
lanes: Lanes,
): Fiber | null {
if (typeof newChild === 'object' && newChild !== null) {
switch (newChild.$$typeof) {
// 单个非文本节点
case REACT_ELEMENT_TYPE:
return placeSingleChild(
reconcileSingleElement(
returnFiber,
currentFirstChild,
newChild,
lanes,
),
);
}
// array类型
if (isArray(newChild)) {
return reconcileChildrenArray(
returnFiber,
currentFirstChild,
newChild,
lanes,
);
}
}
// 文本节点
if (
(typeof newChild === 'string' && newChild !== '') ||
typeof newChild === 'number'
) {
return placeSingleChild(
reconcileSingleTextNode(
returnFiber,
currentFirstChild,
'' + newChild,
lanes,
),
);
}
// 没有匹配到的打上删除标记
return deleteRemainingChildren(returnFiber, currentFirstChild);
}
return reconcileChildFibers;
}
placeSingleChild方法,根据是否追踪副作用,决定是否给新Fiber打上Placement标记。 初次渲染阶段只有HostRootFiber的第一个子Fiber会打上Placement标记,其它节点都不会打,从而实现初次渲染的时候只插入一次真实DOM。
function placeSingleChild(newFiber: Fiber): Fiber {
// 单节点 打上Placement标记
if (shouldTrackSideEffects && newFiber.alternate === null) {
newFiber.flags |= Placement;
}
return newFiber;
}
completeWork
当beginWork返回null,进入归阶段。 从下往上根据不同类型的组件做不同处理。直到到达HostRootFiber。然后进入提交阶段。
// 保留核心代码
function completeUnitOfWork(unitOfWork: Fiber): void {
let completedWork = unitOfWork;
do {
const current = completedWork.alternate;
const returnFiber = completedWork.return;
if ((completedWork.flags & Incomplete) === NoFlags) {
let next;
next = completeWork(current, completedWork,
if (next !== null) {
// 产生了新的节点 只有SuspenseComponent会产生,Suspense会单独开一篇,本篇忽略
workInProgress = next;
return;
}
} else {
// 忽略报错情况
}
// 如果completedWork节点有兄弟节点
const siblingFiber = completedWork.sibling;
if (siblingFiber !== null) {
// 有兄弟节点则继续为兄弟节点beginWork
workInProgress = siblingFiber;
return;
}
// 否则就继续为父节点执行completeUnitOfWork
completedWork = returnFiber;
workInProgress = completedWork;
} while (completedWork !== null);
}
completeWork,自下而上向上执行,直到HostFiberNode。
- 连接子fiber跟当前fiber。
- 合并子fiber的flags,subtreeFlags到当前fiber的subtreeFlags。
- 合并子fiber lanes到当前fiber的childLanes。
- 给DOM类型和文本类型的Fiber节点创建真实的DOM并维护在stateNode属性上。
// 保留核心代码
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
case IndeterminateComponent:
case FunctionComponent:
bubbleProperties(workInProgress);
return null;
case HostRoot: {
const fiberRoot = (workInProgress.stateNode: FiberRoot);
// 浏览器宿主为空函数
updateHostContainer(current, workInProgress);
bubbleProperties(workInProgress);
return null;
}
case HostComponent: {
const rootContainerInstance = getRootHostContainer();
const type = workInProgress.type;
if (current !== null && workInProgress.stateNode != null) {
// 收集html属性修改,并挂到updateQueue上。
updateHostComponent(
current,
workInProgress,
type,
newProps,
rootContainerInstance,
);
if (current.ref !== workInProgress.ref) {
markRef(workInProgress);
}
} else {
// 创建dom节点的时候会把fiber 挂在dom的属性上__reactFiber$ 上
// 创建dom 挂在Fiber的stateNode属性上
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
// 把子节点是DOM类Fiber或者文本Fiber的Html插入到父Fiber的DOM里面。这样mount流程归到HostRootFiber的时候已经有了一棵完整的离屏dom树
appendAllChildren(instance, workInProgress, false, false);
workInProgress.stateNode = instance;
// 完成属性初始化
if (
finalizeInitialChildren(
instance,
type,
newProps,
rootContainerInstance,
currentHostContext,
)
) {
markUpdate(workInProgress);
}
}
if (workInProgress.ref !== null) {
markRef(workInProgress);
}
}
bubbleProperties(workInProgress);
return null;
}
case HostText: {
const newText = newProps;
if (current && workInProgress.stateNode != null) {
const oldText = current.memoizedProps;
// 判断文本是否有变更,有的话打上Update标记。
updateHostText(current, workInProgress, oldText, newText);
} else {
const rootContainerInstance = getRootHostContainer();
const currentHostContext = getHostContext();
// 创建一个文本节点。
workInProgress.stateNode = createTextInstance(
newText,
rootContainerInstance,
currentHostContext,
workInProgress,
);
}
bubbleProperties(workInProgress);
return null;
}
// 忽略其他类型
}
}
到这里整个Render阶段就完成了。接下来就是提交阶段。
Commit
因为篇幅原因,提交阶段放到一下篇讲解。
参考
React设计原理 - 卡颂
转载自:https://juejin.cn/post/7397025359532933120