1 )概述
2 )输入
在ReactFiberWorkLoop.js中,承接输入的函数只有scheduleUpdateOnFiber
在 react-reconciler 对外暴露的api函数中,只要涉及到需要改变fiber的操作(无论是首次渲染或后续更新操作)
最后都会间接调用 scheduleUpdateOnFiber
所以scheduleUpdateOnFiber函数是输入链路中的必经之路
//唯一接收输入信号的函数
export function scheduleUpdateOnFiber(
fiber: Fiber,
lane: Lane,
eventTime: number,
) {
// ... 省略部分无关代码
const root = markUpdateLaneFromFiberToRoot(fiber, lane);
// 同步
if (lane === SyncLane) {
if (
(executionContext & LegacyUnbatchedContext) !== NoContext &&
(executionContext & (RenderContext | CommitContext)) === NoContext
) {
// 直接进行fiber构造
performSyncWorkOnRoot(root);
} else {
// 注册调度任务,经过`Scheduler'包的调度,间接进行`fiber构造'
ensureRootIsScheduled(root, eventTime);
}
} else {
// 注册调度任务,经过`Scheduler`包的调度,间接进行`fiber构造`
ensureRootIsScheduled(root, eventTime);
}
}
逻辑进入到scheduleUpdateOnFiber之后,后面有2种可能:
2 )注册调度任务
与输入环节紧密相连,scheduleUpdateOnFiber函数之后,立即进入 ensureRootIsScheduled 函数
// ... 省略部分无关代码
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
//前半部分:判断是否需要注册新的调度
const existingCallbackNode - root. callbackNode;
const nextlanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
);
const newCallbackPriority = returnNextLanesPriority();
if (nextLanes === NoLanes) {
return;
}
if (existingCallbackNode !== null) {
const existingCallbackPriority = root.callbackPriority;
if (existingCallbackPriority === newCallbackPriority) {
return;
}
cancelCallback(existingCallbackNode);
}
// 后半部分:注册调度任务
let newCallbackNode;
if (newCallbackPriority === SyncLanePriority){
newCallbackNode = scheduleSyncCallback(
performSyncWorkOnRoot.bind(null, root),
);
} else if (newCallbackPriority === SyncBatchedLanePriority) {
newCallbackNode = scheduleCallback(
ImmediateSchedulerPriority,
performSyncWorkOnRoot.bind(null, root),
);
} else {
const schedulerPriorityLevel = lanePriorityToSchedulerPriority(
newCallbackPriority,
);
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root),
);
}
root.callbackPriority = newCallbackPriority;
root.callbackNode = newCallbackNode;
}
3 )执行任务回调
任务回调,实陈上就是执行 performSyncWorkOnRoot 或 performConcurrentWorkOnRoot
简单看一下它们的源码将主要逻辑剥离出来,单个函数的代码量并不多
//..,省略部分无关代码
function performSyncWorkOnRoot(root) {
let lanes;
let exitStatus;
lanes = getNextLanes(root, NoLanes);
// 1. fiber树构造
exitStatus = renderRootSync(root, lanes);
// 2. 异常处理:有可能fiber构造过程中出现异常
if (root.tag !== LegacyRoot && exitStatus === RootErrored) {
// ...
}
// 3. 输出:渲染fiber树
const finishedWork: Fiber = (root.current.alternate: any);
root.finishedwork = finishedWork;
root.finishedLanes = lanes;
commitRoot(root);
// 退出前再次检测,是否还有其他更新,是否需要发起新调度
ensureRootIsScheduled(root, now());
return null;
}
performSyncWorkOnRoot 的逻辑很清晰,分为3部分:
fiber 树构造
异常处理: 有可能fiber构造过程中出现异常
调用输出
// ... 省略部分无关代码
function performConcurrentWorkOnRoot(root) {
const originalCallbackNode = root.callbackNode;
// 1、刷新pending状态的effects,有可能某些effect会取消本次任务
const didFlushPassiveEffects = flushPassiveEffects();
if (didFlushPassiveEffects) {
if (root.callbackNode !== originalCallbackNode) {
// 任务被取消,退出调用
return null;
} else {
// Current task was not canceled. Continue.
}
}
// 2.获取本次渲染的优先级
let lanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
);
// 3.构造fiber树
let exitStatus = renderRootConcurrent(root, lanes);
if (
includesSomeLane(
workInProgressRootIncludedLanes,
workInProgressRootUpdatedLanes,
)
) {
// 如果在render过程中产生了新的update,且新update的优先级与最初render的优先级有交集
// 那么最初render无效,丢弃最初render的结果,等待下一次调度
prepareFreshStack(root, NoLanes);
} else if (exitStatus !== RootIncomplete) {
// 4、异常处理:有可能fiber构造过程中出现异常
if (exitStatus == RootErrored) {
// ...
}
const finishedWork: Fiber = (root.current. alternate: any);
root.finishedWork = finishedwork;
root.finishedLanes = lanes;
// 5.输出:渲染fiber树
finishConcurrentRender(root, exitStatus, lanes);
}
// 退出前再次检测,是否还有其他更新,是否需要发起新调度
ensureRootIsScheduled(root, now());
if (root.callbackNode === originalCallbackNode) {
// 渲染被阻断,返回一个新的performConcurrentWorkOnRoot函数。等待下一次调用
return performConcurrentWorkOnRoot.bind(null, root);
}
return null;
}
performConcurrentWorkOnRoot 的逻辑与 performSyncWorkOnRoot 的不同之处在于
对于可中断渲染的支持:
4 )输出
// ... 省略部分无关代码
function commitRootImpl(root, renderPriorityLevel) {
// 设置局部变量
const finishedWork = root.finishedWork;
const lanes - root. finishedLanes;
// 清空FiberRoot对象上的属性
root.finishedWork = null;
root.finishedLanes = NoLanes;
root.callbackNode = null;
// 提交阶段
let firstEffect = finishedWork.firstEffect;
if (firstEffect !== null) {
const prevExecutionContext - executionContext;
executionContext |= CommitContext;
// 阶段1:dom突变之前
nextEffect = firstEffect;
do {
commitBeforeMutationEffects();
} while (nextEffect !== null);
// 阶段2:dom突变,界面发生改变
nextEffect = firstEffect;
do {
commitMutationEffects(root, renderPriorityLevel);
} while (nextEffect !== null);
root.current = finishedWork;
// 阶段3:layout阶段,调用生命周期componentDidUpdate和回调函数等
nextEffect = firstEffect;
do{
commitLayoutEffects(root, lanes);
} while (nextEffect !== null);
nextEffect = null;
executionContext = prevExecutionContext;
}
ensureRootIsScheduled(root, now());
return null;
}