调用 ReactDOM.render App 组件时会创建 FiberRoot -> createHostRootFiber,标记这个 fiber的 tag 为 HostRoot,创建 FiberRoot 的同时创建 RootFiber,对应ReactDOM.render 方法的第二个参数对应的 Dom 节点
function createFiberRoot(
containerInfo: any,
isConcurrent: boolean,
hydrate: boolean,
): FiberRoot {
// Cyclic construction. This cheats the type system right now because
// stateNode is any.
const uninitializedFiber = createHostRootFiber(isConcurrent);
// ....
}
function createHostRootFiber(isConcurrent: boolean): Fiber {
let mode = isConcurrent ? ConcurrentMode | StrictMode : NoContext;
if (enableProfilerTimer && isDevToolsPresent) {
// Always collect profile timings when DevTools are present.
// This enables DevTools to start capturing timing at any point–
// Without some nodes in the tree having empty base times.
mode |= ProfileMode;
}
return createFiber(HostRoot, null, null, mode);
}
第一次执行到 beginWork 时首次更新的是 HostRoot,在 updateHostRoot 中会调用 reconcileChildFibers 调和子节点,在reconcileSingleElement -> createFiberFromElement -> createFiberFromTypeAndProps依次将 children element 创建成对应的 Fiber 对象, 默认 fiberTag 为 IndeterminateComponent,根据 React.createElement 返回的 type 将 fiberTag 标记为对应的组件类型,将创建完的 children 返回到 workLoop,遍历执行 performUnitOfWork -> beginWork, 根据这个 children 的 fiberTag 对应依次更新,如此循环先创建,再更新。
function renderRoot(
root: FiberRoot,
isYieldy: boolean,
isExpired: boolean,
): void {
isWorking = true;
ReactCurrentOwner.currentDispatcher = Dispatcher;
const expirationTime = root.nextExpirationTimeToWorkOn;
// Check if we're starting from a fresh stack, or if we're resuming from
// previously yielded work.
// 说明将要执行的任务 root 和 expirationTime 和 nextRenderExpirationTime、nextRoot 预期的不一样, 可能是之前任务被高优先级的任务打断了。
if (
expirationTime !== nextRenderExpirationTime ||
root !== nextRoot ||
nextUnitOfWork === null // 更新结束 fiber 的 child,下一个节点, 首次为 null
) {
// Reset the stack and start working from the root.
// 重置
resetStack();
nextRoot = root;
// root.nextExpirationTimeToWorkOn;
nextRenderExpirationTime = expirationTime;
// 将 fiber 拷贝成 workInProgress 对象操作,不直接操作 fiber 节点
nextUnitOfWork = createWorkInProgress(
nextRoot.current,
null,
nextRenderExpirationTime,
);
root.pendingCommitExpirationTime = NoWork;
let didFatal = false;
startWorkLoopTimer(nextUnitOfWork);
// 开始循环 workLoop
do {
try {
// 同步或过期任务则 isYieldy 为 false 表示不可中断
workLoop(isYieldy);
} catch (thrownValue) {
// ... catch 错误
// 致命错误
if (didFatal) { }
// workLoop break 中断的错误
if (nextUnitOfWork !== null) { }
// 可处理的错误
if (nextRenderDidError) { }
}
break; // 遇到了某种错误跳出
} while (true);
// ...
const rootWorkInProgress = root.current.alternate;
// Ready to commit.
// 最后 commit root
onComplete(root, rootWorkInProgress, expirationTime);
}
workLoop
根据 isYieldy 不同执行 performUnitOfWork
function workLoop(isYieldy) {
// 同步或过期任务 isYieldy 为false 不可中断
if (!isYieldy) {
// Flush work without yielding
while (nextUnitOfWork !== null) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
} else {
// Flush asynchronous work until the deadline runs out of time.
// !shouldYield() 还有剩余时间用来更新
while (nextUnitOfWork !== null && !shouldYield()) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
}
}
performUnitOfWork
next 变量赋值为 beginWork 的返回值,即更新完之后会返回它的下一个节点
function performUnitOfWork(workInProgress: Fiber): Fiber | null {
const current = workInProgress.alternate;
// See if beginning this work spawns more work.
startWorkTimer(workInProgress);
let next;
if (enableProfilerTimer) {
if (workInProgress.mode & ProfileMode) {
startProfilerTimer(workInProgress);
}
// 执行对整个树每个节点进行更新的操作,赋值 next 为更新完后的下一个节点
next = beginWork(current, workInProgress, nextRenderExpirationTime);
// 将最新的 props 赋值给目前正在用的 props
workInProgress.memoizedProps = workInProgress.pendingProps;
if (workInProgress.mode & ProfileMode) {
// Record the render duration assuming we didn't bailout (or error).
stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true);
}
} else {
next = beginWork(current, workInProgress, nextRenderExpirationTime);
workInProgress.memoizedProps = workInProgress.pendingProps;
}
// 更新到最后的节点,叶子节点
if (next === null) {
// If this doesn't spawn new work, complete the current work.
// 往上遍历
next = completeUnitOfWork(workInProgress);
}
ReactCurrentOwner.current = null;
return next;
}
beginWork
判断当前 fiber 树是否需要更新,不需要更新的则进行优化
根据节点类型进行对应的更新
更新后调和子节点
function beginWork(
// RootFiber
current: Fiber | null,
// 与 nextUnitOfWork 对应,下一个将要更新的节点
workInProgress: Fiber,
// root.nextExpirationTimeToWorkOn 表示一次更新内最高优先级的更新
renderExpirationTime: ExpirationTime,
): Fiber | null {
// 节点自身的过期时间
const updateExpirationTime = workInProgress.expirationTime;
if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (
// 前后两次props 相同
oldProps === newProps &&
// context 相关
!hasLegacyContextChanged() &&
// 没有更新
(updateExpirationTime === NoWork ||
// 更新优先级没有当前更新优先级高
updateExpirationTime > renderExpirationTime)
) {
// This fiber does not have any pending work. Bailout without entering
// the begin phase. There's still some bookkeeping we that needs to be done
// in this optimized path, mostly pushing stuff onto the stack.
switch (workInProgress.tag) {
// ...优化的过程
}
// 跳过当前节点及其子节点的更新
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderExpirationTime,
);
}
}
// Before entering the begin phase, clear the expiration time.
workInProgress.expirationTime = NoWork;
// 节点是有更新的情况
switch (workInProgress.tag) {
// ...根据节点类型,执行对应组件类型的更新
}
}
function bailoutOnAlreadyFinishedWork(
current: Fiber | null,
workInProgress: Fiber,
renderExpirationTime: ExpirationTime,
): Fiber | null {
cancelWorkTimer(workInProgress);
if (current !== null) {
// Reuse previous context list
workInProgress.firstContextDependency = current.firstContextDependency;
}
// Check if the children have any pending work.
// 子节点最高优先级的更新
const childExpirationTime = workInProgress.childExpirationTime;
if (
// 没有更新
childExpirationTime === NoWork ||
// 子树的优先级低
childExpirationTime > renderExpirationTime
) {
// The children don't have any work either. We can skip them.
// TODO: Once we add back resuming, we should check if the children are
// a work-in-progress set. If so, we need to transfer their effects.
// 直接跳过子树更新
return null;
} else {
// This fiber doesn't have work, but its subtree does. Clone the child
// fibers and continue.
// 子树有更新的情况
// 当前节点不需要更新,说明 child 没有更新,把老的 workInProgress.child 复制一份,并返回
cloneChildFibers(current, workInProgress);
return workInProgress.child;
}
}
renderRoot
调用
ReactDOM.render App
组件时会创建FiberRoot -> createHostRootFiber
,标记这个 fiber的 tag 为HostRoot
,创建FiberRoot
的同时创建RootFiber
,对应ReactDOM.render
方法的第二个参数对应的 Dom 节点beginWork
时首次更新的是HostRoot
,在updateHostRoot
中会调用reconcileChildFibers
调和子节点,在reconcileSingleElement -> createFiberFromElement -> createFiberFromTypeAndProps
依次将children element
创建成对应的Fiber
对象, 默认fiberTag
为IndeterminateComponent
,根据React.createElement
返回的type
将fiberTag
标记为对应的组件类型,将创建完的children
返回到workLoop
,遍历执行performUnitOfWork -> beginWork
, 根据这个children
的fiberTag
对应依次更新,如此循环先创建,再更新。workLoop
进行循环单元更新, 对整棵 fiberTree 都遍历一遍nextUnitOfWork
是每个节点自己更新完之后返回的第一个子节点nextUnitOfWork
首次赋值为createWorkInProgress
拷贝的一份fiber
节点,以后的操作都是修改的 nextUnitOfWork, 防止改变当前 fiberTreeworkLoop
performUnitOfWork
next 变量赋值为 beginWork 的返回值,即更新完之后会返回它的下一个节点
beginWork
fiber
树是否需要更新,不需要更新的则进行优化bailoutOnAlreadyFinishedWork
fiber
的子树是否需要更新,如果需要更新则会 clone 一份老的 child 到workInProgress.child
返回到performUnitOfWork
的next
再返回到workLoop
的nextUnitOfWork
循环更新子节点, 否则为return null
根据 fiber 的 tag 类型进行更新
fiber
的expirationTIme
置为Nowork
workInProgress.type
函数式组件为function``,
class组件为
class` 构造函数,dom原生组件为div这种字符串penddingProps
,新的渲染产生的props
updateHostRoot
一个 React 应用中一般只有一个
hostRoot
,对应的Fiber
为RootFiber
对象, 对应的element
为ReactDOM.render
方法的第二个参数<div id="root" />
, 也就是 FiberRoot。创建更新的过程,
ReactDOM.render
->ReactRoot.prototype.render
->DOMRenderer.updateContainer
->updateContainerAtExpirationTime
->scheduleRootUpdate
processUpdateQueue
得到一个element
属性新state
,从这个 element 开始创建和更新整个 fiber tree 。