updateContainer会调用 updateContainerAtExpirationTime ,初始更新是同步的所以expirationTime都是1073741823。updateContainerAtExpirationTime 传入的参数如下:
function updateContainer(element, container, parentComponent, callback){
return updateContainerAtExpirationTime(element, container,
parentComponent, expirationTime, callback);
}
此时element是children;container是root(ReactRoot);parentComponent是null;expirationTime是1073741823;callback是work._onCommit。
在 updateContainerAtExpirationTime 中安排Root的更新,要注意的是current 是ReactRoot.current中的HostRootFiber。在这个方法中会对 ReactRoot的context、pendingContext属性中的一项进行修改。
export function updateContainerAtExpirationTime(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
expirationTime: ExpirationTime,
callback: ?Function,
) {
// TODO: If this is a nested container, this won't be the root.
const current = container.current;
const context = getContextForSubtree(parentComponent);
if (container.context === null) {
container.context = context;
} else {
container.pendingContext = context;
}
return scheduleRootUpdate(current, element, expirationTime, callback);
}
执行到此,调用栈如下:updateContainer -> updateContainerAtExpirationTime -> scheduleRootUpdate。
scheduleRootUpdate
这个方法中是计划对Root的update,此时还没有开始work。
function scheduleRootUpdate(
current: Fiber,
element: ReactNodeList,
expirationTime: ExpirationTime,
callback: ?Function,
) {
const update = createUpdate(expirationTime);
update.payload = {element}; //要更新的元素
callback = callback === undefined ? null : callback;
if (callback !== null) {
update.callback = callback;
}
flushPassiveEffects();//刷新effect
enqueueUpdate(current, update);//更新队列
scheduleWork(current, expirationTime);//计划work
return expirationTime;
}
//创建的是更新对象
export function createUpdate(expirationTime: ExpirationTime): Update<*> {
return {
expirationTime: expirationTime,
tag: UpdateState,//更新的标签
payload: null,
callback: null,
next: null,
nextEffect: null,
};
}
在enqueueUpdate 主要做的是append UpdateToQueue,队列传送门。若无队列则会createUpdateQueue,有队列则会cloneUpdateQueue。在此scheduleRootUpdate中是将update放入queue 。完成 flushPassiveEffects 和 enqueueUpdate 后就开始 scheduleWork。
scheduleWork
这个方法中会检查是否有working和,若有则中断fiber。markPendingPriorityLevel 中计算expirationTime和nextExpirationTimeToWorkOn,这有点 time slicing的味道。此处优先梳理Initial mount。
function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
const root = scheduleWorkToRoot(fiber, expirationTime);//根据fiber找root
if (root === null) { return; }
if (!isWorking &&
nextRenderExpirationTime !== NoWork &&
expirationTime > nextRenderExpirationTime
) {
// This is an interruption. (Used for performance tracking.) //中断
interruptedBy = fiber;
resetStack();
}
markPendingPriorityLevel(root, expirationTime);//有点 time slicing 的味道
if (
// If we're in the render phase, we don't need to schedule this root
// for an update, because we'll do it before we exit...
!isWorking ||
isCommitting ||
// ...unless this is a different root than the one we're rendering.
nextRoot !== root
) {
const rootExpirationTime = root.expirationTime;
requestWork(root, rootExpirationTime);
}
if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
// Reset this back to zero so subsequent updates don't throw.
nestedUpdateCount = 0;
}
}
scheduleWorkToRoot 主要目的是找到ReactRoot。同时为 fiber.expirationTime、 alternate.expirationTime添加过期时间,在查找ReactRoot的同时,为当前fiber.return的childExpirationTime 添加过期时间。在更新阶段,由于过期时间不为0,更新当前fiber。
requestWork
在requestWork 会判断是批处理、同步处理、按时间处理。请求 work,但并不一定可以work。isBatchingUpdates 时只是addRootToSchedule,但不进行work。addRootToSchedule 会建立一个链表,在findHighestPriorityRoot中会使用这个链表。Initial mount时,直接performSyncWork。
function requestWork(root: FiberRoot, expirationTime: ExpirationTime) {
addRootToSchedule(root, expirationTime);//添加到计划中
if (isRendering) {
// Prevent reentrancy. Remaining work will be scheduled at the end of
// the currently rendering batch.
return;
}
if (isBatchingUpdates) {// 事件是批处理----事件的批处理和此处有点关系
// Flush work at the end of the batch.
if (isUnbatchingUpdates) {
// ...unless we're inside unbatchedUpdates, in which case we should
// flush it now.
nextFlushedRoot = root;
nextFlushedExpirationTime = Sync;//过期时间变为同步
performWorkOnRoot(root, Sync, false);
}
return;
}
if (expirationTime === Sync) {//初始时 expirationTime 是 Sync
performSyncWork();
} else {
scheduleCallbackWithExpirationTime(root, expirationTime);
}
}
init mount执行addRootToSchedule 后就开始执行 performSyncWork。performSyncWork 之后是:
performSyncWork —> performWork(Sync, false) —> findHighestPriorityRoot()、performWorkOnRoot ;
performWork
function performWork(minExpirationTime: ExpirationTime, isYieldy: boolean) {
findHighestPriorityRoot();//找到 addRootToSchedule 中存放的值
//...
while (
nextFlushedRoot !== null &&
nextFlushedExpirationTime !== NoWork &&
minExpirationTime <= nextFlushedExpirationTime
) {
performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
findHighestPriorityRoot();
}
//...
// Clean-up.
finishRendering();
}
findHighestPriorityRoot 找到 nextFlushedRoot 、nextFlushedExpirationTime,若没有则直接 finishRendering。在performWork中执行performWorkOnRoot是在while循环中,说明可能会有多个root。
performWorkOnRoot
function performWorkOnRoot(
root: FiberRoot,
expirationTime: ExpirationTime,
isYieldy: boolean,
) {
isRendering = true; //开始rendering
// Check if this is async work or sync/expired work.
if (!isYieldy) {
// Flush work without yielding.
let finishedWork = root.finishedWork;
if (finishedWork !== null) {
// This root is already complete. We can commit it.
completeRoot(root, finishedWork, expirationTime);
} else {
root.finishedWork = null;
// If this root previously suspended, clear its existing timeout, since
// we're about to try rendering again.
const timeoutHandle = root.timeoutHandle;
if (timeoutHandle !== noTimeout) {
root.timeoutHandle = noTimeout;
// $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
cancelTimeout(timeoutHandle);
}
//*********************************************
renderRoot(root, isYieldy);//生成React Tree Reconciliation
finishedWork = root.finishedWork;
if (finishedWork !== null) {
// We've completed the root. Commit it.
completeRoot(root, finishedWork, expirationTime);
}
//*********************************************
}
}
//...省略异步代码
isRendering = false;
}
在performWorkOnRoot 中开始rendering。若 finishedWork有值则直接 completeRoot,这是fibers的复用;若无finishedWork值则renderRoot -> completeRoot。在 performWorkOnRoot 中的 renderRoot 生成 React Tree Reconciliation,completeRoot 则是commit阶段。执行到此时,调用的栈为:
renderRoot
进入 renderRoot 中就进入working。之前scheduleWork在此正式开始。此处renderRoot代码只是摘要的,具体看renderRoot源码。
function renderRoot(root: FiberRoot, isYieldy: boolean): void {
flushPassiveEffects();
isWorking = true;
var didFatal = false;
startWorkLoopTimer(nextUnitOfWork);
// Check if we're starting from a fresh stack, or if we're resuming from
// previously yielded work.
if (
expirationTime !== nextRenderExpirationTime ||
root !== nextRoot ||
nextUnitOfWork === null
) {
// Reset the stack and start working from the root.
resetStack();
nextRoot = root;
nextRenderExpirationTime = expirationTime;
nextUnitOfWork = createWorkInProgress(nextRoot.current,null,nextRenderExpirationTime);
root.pendingCommitExpirationTime = NoWork;
}
startWorkLoopTimer(nextUnitOfWork); //开始 "(React Tree Reconciliation)"
do {
try { workLoop(isYieldy); } catch (thrownValue) {//异常处理,此处省略 }
break;
} while (true);
// We're done performing work. Time to clean up.
isWorking = false;
ReactCurrentDispatcher.current = null;
resetContextDependences();
resetHooks();
// We completed the whole tree.
const didCompleteRoot = true;
stopWorkLoopTimer(interruptedBy, didCompleteRoot);// 删除 "(React Tree Reconciliation)"
// Ready to commit.
onComplete(root, rootWorkInProgress, expirationTime);
}
function onComplete(root, finishedWork, expirationTime) {
root.pendingCommitExpirationTime = expirationTime;
root.finishedWork = finishedWork;
}
createWorkInProgress会创建fiber为current.alternate赋值并克隆当前fiber(HostRootFiber)的属性。在 startWorkLoopTimer中会标记”(React Tree Reconciliation)”,标志着开始生成React Tree Reconciliation;在 stopWorkLoopTimer 中会清除 “(React Tree Reconciliation)” 标记,标志着完成 React Tree Reconciliation。这中间就执行了WorkLoop方法。
workLoop示意图
workLoop中完成所有的work,并生成 React Tree 树。我们先来看一下workLoop的执行方式。此图有缺陷——就是假设子元素都是一次性生成的。 一般初始时fiber是RootFiber,由此开始生成React Tree。fiber是增量渲染,React Tree是逐渐生成的。
workLoop流程
我们假设当前有上图结构的树。
第一次执行时nextUnitOfWork是ReactRoot(fiberRooot),执行 performUnitOfWork -> beginWork生成a1,并返回a1
第二次执行时nextUnitOfWork 是a1,执行 performUnitOfWork -> beginWork 生成b1、b2、b3,并返回b1(next=b1);
第三次执行 nextUnitOfWork 是b1,执行 performUnitOfWork -> beginWork,返回null,执行performUnitOfWork -> completeUnitOfWork -> completeWork (b2),并返回兄弟节点b2(next=b2);
第四次执行 nextUnitOfWork 是b2,执行performUnitOfWork -> beginWork 生成c1,并返回c1(next=c1);
第五次执行 nextUnitOfWork 是c1,执行performUnitOfWork -> beginWork 生成d1、d2,并返回d1(next=d1);
第六次执行 nextUnitOfWork 是d1,执行 performUnitOfWork -> beginWork,返回null,执行performUnitOfWork -> completeUnitOfWork -> completeWork(d1),并返回兄弟节点d2(next=d2);
第七次执行 nextUnitOfWork 是d2,执行 performUnitOfWork -> beginWork,返回null,执行performUnitOfWork -> completeUnitOfWork -> completeWork 结束当前d2的work。completeUnitOfWork是一个while循环,d2无兄弟节点,则workInProgress指向c1,并continue completeUnitOfWork循环,进入下一次while循环。接着的执行completeUnitOfWork -> completeWork(c1),c1无兄弟节点,则workInProgress指向b2,并continue completeUnitOfWork循环,进入下一次while循环;接着执行completeUnitOfWork -> completeWork(b2),并返回兄弟节点b3(next=b3);
第八次执行 nextUnitOfWork 是b3,执行 performUnitOfWork -> beginWork 生成c2,并返回c2(next=c2);
第九次执行 nextUnitOfWork 是c2,执行 performUnitOfWork -> beginWork,返回null,执行performUnitOfWork -> completeUnitOfWork -> completeWork 结束当前c2的work。completeUnitOfWork是一个while循环,c2无兄弟节点,则workInProgress指向b3,并continue completeUnitOfWork循环,进入下一次while循环。接着的执行completeUnitOfWork -> completeWork(b3),b3无兄弟节点,则workInProgress指向a1; 接着的执行completeUnitOfWork -> completeWork(a1),a1无兄弟节点也无父节点,则return null,结束循环;
这个流程只是说明workLoop的执行顺序,在实际渲染时渲染节点是需要区分节点类型,这些类型在beginWork中处理。
workLoop的代码
每一个ReactElement是一个fiber,每一fiber又是一个work。fiber的多少根jsx有关系,执行jsx时有多少个子元素就有多少个子fiber。performUnitOfWork 中返回的是next unit of work。
function workLoop(isYieldy) {
if (!isYieldy) {
// Flush work without yielding
while (nextUnitOfWork !== null) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
} else {// Flush asynchronous work until there's a higher priority event
while (nextUnitOfWork !== null && !shouldYieldToRenderer()) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
}
}
performUnitOfWork 用于完成 Unit Of Work,同时并返回next,主要方法是beginWork。
function performUnitOfWork(workInProgress: Fiber): Fiber | null {
// The current, flushed, state of this fiber is the alternate.
// Ideally nothing should rely on this, but relying on it here
// means that we don't need an additional field on the work in
// progress.
const current = workInProgress.alternate;//此处 alternate
// See if beginning this work spawns more work.
startWorkTimer(workInProgress);
let next;
if (enableProfilerTimer) {
if (workInProgress.mode & ProfileMode) {
startProfilerTimer(workInProgress);
}
next = beginWork(current, workInProgress, nextRenderExpirationTime);
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中的 current 是 workInProgress.alternate,workInProgress是创建的fiber。