这大概是 React 源码中我看到过最长的方法代码了。。。这一步主要 做的事情就是根据当前节点类型,生成Fiber; 这里展示的我把一些不是特别重要的比如提醒代码或者开发工具代码删了一部分,还有注释也删了挺多。
renderRoot
这里是hooks相关的实现,我们后续在讲:
typescript
const previousDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = ContextOnlyDispatcher;首先是一个判断是否需要初始化变量的判断
typescript
if (
expirationTime !== nextRenderExpirationTime ||
root !== nextRoot ||
nextUnitOfWork === null
) {
resetStack();
nextRoot = root;
nextRenderExpirationTime = expirationTime;
nextUnitOfWork = createWorkInProgress(
nextRoot.current,
null,
nextRenderExpirationTime,
);
root.pendingCommitExpirationTime = NoWork;
}那么这个判断是什么意思呢?他判断的情况是是否有新的更新进来了。假设这种情况:上一个任务因为时间片用完了而中断了,这个时候nextUnitOfWork是有工作的,这时候如果下一个requestIdleCallback进来了,中途没有新的任务进来,那么这些全局变量都没有变过,root的nextExpirationTimeToWorkOn肯定也没有变化,那么代表是继续上一次的任务。而如果有新的更新进来,则势必nextExpirationTimeToWorkOn或者root会变化,那么肯定需要重置变量
然后就进入整体,执行 workLoop
typescript
do {
try {
workLoop(isYieldy);
} catch (thrownValue) {
// 处理错误相关
}
} while (true);workLoop
typescript
function workLoop(isYieldy) {
if (!isYieldy) {
while (nextUnitOfWork !== null) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
} else {
// 对于异步的情况 会判断时间片是否还有剩余时间
while (nextUnitOfWork !== null && !shouldYieldToRenderer()) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
}
}performUnitOfWork
首先大概介绍一下performUnitOfWork方法做的事情
- 调用beginWork, 调和子节点,生成Fiber对象
- 当next为null, 说明处理到了当前树的最深节点,然后调用
completeUnitOfWork completeUnitOfWork方法里面,创建了真实的dom节点(只是创建,还没渲染到页面),其次,将节点的副作用都往父节点上挂载,最后在commit阶段进行真实的操作
typescript
function performUnitOfWork(workInProgress: Fiber): Fiber | null {
// 对于FiberRoot 在renderRoot的时候 立马创建了 workInProgress ,
// workInProgress.alternate 就是Fiber对象
// 处理FiberRoot一开始current是有值的 其他的节点一开始current都是没有值的
// 除非第一次渲染完后,在第二次渲染的时候就会都有值
const current = workInProgress.alternate;
// 开始执行beginWork
let next = beginWork(current, workInProgress, nextRenderExpirationTime);
// 更新完后将pendingProps赋值给memoizedProps
workInProgress.memoizedProps = workInProgress.pendingProps;
if (next === null) {
// 如果next是null 执行completeUnitOfWork ,完成这个单元的更新
// completeUnitOfWork会向上返回父节点或者兄弟节点
// 当next真的为null跳出workLoop的循环后,说明当前到了FiberRoot
next = completeUnitOfWork(workInProgress);
}
return next;
}beginWork
beginWork代码分为2个大部分,第一个是节点的优化,第二部分是不同类型的组件的处理
typescript
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderExpirationTime: ExpirationTime,
): Fiber | null {
// 对于RootFiber节点,只有调用ReactDOM.render 才会在RootFiber节点上产生更新
// 所以对于RootFiber ,updateExpirationTime才会有值
// 对于普通的Fiber。updateExpirationTime有值说明是当前Fiber上产生的更新
const updateExpirationTime = workInProgress.expirationTime;
// 第一次渲染 RooFiber的current是有值的,其他的节点第一次是没有值得
// 对于其他节点 第一次渲染都是没有的 都是null
if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (oldProps !== newProps || hasLegacyContextChanged()) {
// If props or context changed, mark the fiber as having performed work.
// This may be unset if the props are determined to be equal later (memo).
didReceiveUpdate = true;
} else if (updateExpirationTime < renderExpirationTime) {
// oldProps === newProps 相等
// 如果当前Fiber上的更新的时间小于FiberRoot的更新时间
// 说明当前Fiber在这一次不需要渲染
// 是可以跳过的
didReceiveUpdate = false;
switch (workInProgress.tag) {
// ...
}
// 会跳过当前节点和所有子节点的更新
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderExpirationTime,
);
}
} else {
didReceiveUpdate = false;
}
}bailoutOnAlreadyFinishedWork
typescript
function bailoutOnAlreadyFinishedWork(
current: Fiber | null,
workInProgress: Fiber,
renderExpirationTime: ExpirationTime,
): Fiber | null {
const childExpirationTime = workInProgress.childExpirationTime;
if (childExpirationTime < renderExpirationTime) {
// 代表子树没有更新需要在这一次渲染中去完成的
// 直接return null 跳过整个子树的更新判断
return null;
} else {
// 如果走到这 说明子树在当前有更新是要执行的
cloneChildFibers(current, workInProgress);
return workInProgress.child;
}
}completeUnitOfWork
typescript
function completeUnitOfWork(workInProgress: Fiber): Fiber | null {
while (true) {
const current = workInProgress.alternate;
// 父节点
const returnFiber = workInProgress.return;
// 兄弟节点
const siblingFiber = workInProgress.sibling;
if ((workInProgress.effectTag & Incomplete) === NoEffect) {
// 进入到这 说明当前Fiber上没有 Incomplete tag, 也就是没有出现错误被捕获的情况
// This fiber completed.
// Remember we're completing this unit so we can find a boundary if it fails.
nextUnitOfWork = workInProgress;
if (enableProfilerTimer) {
// ...
} else {
nextUnitOfWork = completeWork(
current,
workInProgress,
nextRenderExpirationTime,
);
}
stopWorkTimer(workInProgress);
// 当前节点complete完 说明有高优先级的子节点已经更新完了
// 那么就需要更新 childExpirationTime
// 向上冒泡子节点的最高优先级的expirationTime
resetChildExpirationTime(workInProgress, nextRenderExpirationTime);
if (nextUnitOfWork !== null) {
// Completing this fiber spawned new work. Work on that next.
return nextUnitOfWork;
}
if (
returnFiber !== null &&
// Do not append effects to parents if a sibling failed to complete
(returnFiber.effectTag & Incomplete) === NoEffect
) {
// 父节点存在,父节点没有 Incomplete tag, 也就是没有出现错误被捕获的情况
// 然后将当前节点的firstEffect到lastEffect链到父节点上,一直往上赶effect
// 直到最后,effect都到了RootFiber节点上
if (returnFiber.firstEffect === null) {
returnFiber.firstEffect = workInProgress.firstEffect;
}
if (workInProgress.lastEffect !== null) {
if (returnFiber.lastEffect !== null) {
returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
}
returnFiber.lastEffect = workInProgress.lastEffect;
}
const effectTag = workInProgress.effectTag;
// PerformedWork 的值是1,如果当前Fiber有更新,那么当前Fiber的tag肯定大于1
// 也就是说 当前Fiber有更新或者说是删除等等effect存在,那么当前Fiber也要添加到
// 父节点的Effect链上
if (effectTag > PerformedWork) {
if (returnFiber.lastEffect !== null) {
returnFiber.lastEffect.nextEffect = workInProgress;
} else {
returnFiber.firstEffect = workInProgress;
}
returnFiber.lastEffect = workInProgress;
}
}
if (siblingFiber !== null) {
// 如果兄弟节点存在,那么返回兄弟节点,然后跳出循环
// 兄弟节点继续进行beginWork
return siblingFiber;
} else if (returnFiber !== null) {
// If there's no more work in this returnFiber. Complete the returnFiber.
// 如果兄弟节点不存在,并且父节点存在,那么就继续将父节点执行completeUnitOfWork
workInProgress = returnFiber;
continue;
} else {
// We've reached the root.
// 如果兄弟节点和父节点都不存在,说明到RootFiber了,可以直接跳出循环了
return null;
}
} else {
// 出现了错误被捕获的情况
if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
// ...
}
const next = unwindWork(workInProgress, nextRenderExpirationTime);
// Because this fiber did not complete, don't reset its expiration time.
if (workInProgress.effectTag & DidCapture) {
// Restarting an error boundary
stopFailedWorkTimer(workInProgress);
} else {
stopWorkTimer(workInProgress);
}
if (next !== null) {
stopWorkTimer(workInProgress);
next.effectTag &= HostEffectMask;
return next;
}
if (returnFiber !== null) {
// Mark the parent fiber as incomplete and clear its effect list.
returnFiber.firstEffect = returnFiber.lastEffect = null;
returnFiber.effectTag |= Incomplete;
}
if (siblingFiber !== null) {
// If there is more work to do in this returnFiber, do that next.
return siblingFiber;
} else if (returnFiber !== null) {
// If there's no more work in this returnFiber. Complete the returnFiber.
workInProgress = returnFiber;
continue;
} else {
return null;
}
}
}
return null;
}resetChildExpirationTime
typescript
function resetChildExpirationTime(
workInProgress: Fiber,
renderTime: ExpirationTime,
) {
if (renderTime !== Never && workInProgress.childExpirationTime === Never) {
// The children of this component are hidden. Don't bubble their
// expiration times.
return;
}
let newChildExpirationTime = NoWork;
// Bubble up the earliest expiration time.
// 向上冒泡子节点的最高优先级的expirationTime
if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
// ...
} else {
let child = workInProgress.child;
while (child !== null) {
const childUpdateExpirationTime = child.expirationTime;
const childChildExpirationTime = child.childExpirationTime;
if (childUpdateExpirationTime > newChildExpirationTime) {
newChildExpirationTime = childUpdateExpirationTime;
}
if (childChildExpirationTime > newChildExpirationTime) {
newChildExpirationTime = childChildExpirationTime;
}
child = child.sibling;
}
}
workInProgress.childExpirationTime = newChildExpirationTime;
}