上次我们分析到了updateContainer方法,本节继续跟踪 在上节给出的updateContainer简化版的代码中,有如下几个重要的步骤:
const update = createUpdate(eventTime, lane)
update.payload = {element};
enqueueUpdate(current, update);
scheduleUpdateOnFiber(current, lane, eventTime);
createUpdate的逻辑很简单,就是创建了一个描述更新的对象
export function createUpdate(eventTime: number, lane: Lane): Update<*> {
const update: Update<*> = {
eventTime,
lane,
tag: UpdateState,
payload: null,
callback: null,
next: null,
};
return update;
}
在enqueueUpdate方法中,就是将刚刚创建的update放入到fiber的updateQueue中
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
const updateQueue = fiber.updateQueue;
if (updateQueue === null) {
// Only occurs if the fiber has been unmounted.
return;
}
const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
const pending = sharedQueue.pending;
if (pending === null) {
// This is the first update. Create a circular list.
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
sharedQueue.pending = update;
这里的sharedQueue.pending,构成了一个单向的环形链表
接下来的scheduleUpdateOnFiber方法,里面判断很多,但无论是哪种判断,基本可归结为两条路径:
performSyncWorkOnRoot
ensureRootIsScheduled -> performSyncWorkOnRoot / performConcurrentWorkOnRoot
在performSyncWorkOnRoot方法里,又有两个主要步骤:
exitStatus = renderRootSync(root, lanes);
commitRoot(root);
renderRootSync简化后代码如下:
function renderRootSync(root: FiberRoot, lanes: Lanes) {
const prevExecutionContext = executionContext;
executionContext |= RenderContext;
const prevDispatcher = pushDispatcher();
// If the root or lanes have changed, throw out the existing stack
// and prepare a fresh one. Otherwise we'll continue where we left off.
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
prepareFreshStack(root, lanes);
startWorkOnPendingInteractions(root, lanes);
}
do {
try {
workLoopSync();
break;
} catch (thrownValue) {
handleError(root, thrownValue);
}
} while (true);
workInProgressRoot = null;
workInProgressRootRenderLanes = NoLanes;
return workInProgressRootExitStatus;
}
renderRootSync里面,主要做的事情就是通过调用
workLoopSync();
开启了事件循环 workLoopSync主要做的工作就是创建fiber并收集effect
function workLoopSync() {
// Already timed out, so perform work without checking if we need to yield.
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
renderRootSync里还有一个工作很关键: 在最开始,通过调用prepareFreshStack给workingInProgress赋值,将workingInProgress作为构建新的fiber树的起点
prepareFreshStack中给workInProgress赋值的语句是:
workInProgress = createWorkInProgress(root.current, null);
createWorkInProgress其实就是复用现有的rootFiber,创建一个新的rootFiber
renderRootSync执行完回到performSyncWorkOnRoot之后,就会将Fiber构建好(beginWork),同时也在fiberRoot上收集好了effectList(completeWork)
然后在接下来的commitRoot方法中,遍历执行所有的effect,创建真实DOM
在commitRoot中,实际上还做了一些执行优先级相关的工作,内部又调用了commitRootImpl方法:
function commitRoot(root) {
const renderPriorityLevel = getCurrentPriorityLevel();
runWithPriority(
ImmediateSchedulerPriority,
commitRootImpl.bind(null, root, renderPriorityLevel),
);
return null;
}
在commitRootImpl中,开启了三轮循环,分别从firstEffect开始,循环到lastEffect为止 三轮循环分别执行了
commitBeforeMutationEffects()
commitMutationEffects(root, renderPriorityLevel)
commitLayoutEffects(root, lanes)
实际在这个过程之前,还做了一些事情,例如useEffect的回调等处理
在before阶段,即commitBeforeMutationEffects,做的事情有:
- 调用getSnapshotBeforeUpdate生命周期钩子
- 调度useEffect
commitLayoutEffects一共做了两件事:
- commitLayoutEffectOnFiber(调用生命周期钩子例如componentDidMount componentDidUpdate,setState里的回调,以及hook相关操作)
- commitAttachRef(赋值 ref)