React源码之看完吊打面试官系列
经历一个月的学习整理,站在前人的肩膀上,对React有了一些浅薄的理解,希望记录自己的学习过程的同时也可以给大家带来一点小帮助。如果此系列文章对您有些帮助,还望在座各位义夫义母不吝点赞关注支持🐶,也希望各位大佬拍砖探讨
本系列行文思路如下,本篇属于React中的React的管理员(reconciler与scheduler)
-
[X] React启动过程 -
[X] React的两大工作循环 -
[X] React中的对象 -
[ ] React fiber的初次创建与更新 -
[ ] React fiber的渲染 -
[X] React的管理员(reconciler与scheduler) -
[X] react的优先级管理(Lane模型)
「React Hook原理」
-
[ ] 状态与副作用 -
[ ] Hook原理 -
[ ] 状态Hook -
[ ] 副作用Hook
「其他」
-
[ ] React的合成事件 -
[ ] Context原理 -
[ ] diff算法
「React源码解析之Reconciler运行循环」
Reconciler运行循环概览

reconciler
包的主要作用,将主要功能分为4个方面:
-
「输入」:暴露api函数(如:scheduleUpdateOnFiber),提供给其他包(如react包)调用.(所有的输入的必经之路) -
「注册调度任务」:与调度中心(scheduler包)交互,注册调度任务task,等待任务回调. -
「执行任务回调」:在内存中构造出fiber树,同时与渲染器(react-dom)交互,在内存中创建出与fiber对应的DOM节点. -
「输出」: 与渲染器(react-dom)交互,渲染DOM节点
输入
在 ReactFiberWorkLoop.js
中, 承接输入的函数只有 scheduleUpdateOnFiber
在 react-reconciler
对外暴露的 api 函数中, 只要涉及到需要改变 fiber 的操作(无论是首次渲染或后续更新操作), 最后都会间接调用 scheduleUpdateOnFiber
, 所以 scheduleUpdateOnFiber
函数是「输入链路中的必经之路」.
// 唯一接收输入信号的函数
export function scheduleUpdateOnFiber(
fiber: Fiber,
lane: Lane,
eventTime: number,
) {
// ... 省略部分无关代码
//给fiber节点添加优先级
const root = markUpdateLaneFromFiberToRoot(fiber, lane);
if (lane === SyncLane) {
if (
(executionContext & LegacyUnbatchedContext) !== NoContext &&
(executionContext & (RenderContext | CommitContext)) === NoContext
) {
// 直接进行`fiber构造`
// 条件是NoContext,一般是初次构造,不经过调度
performSyncWorkOnRoot(root);
} else {
// 注册调度任务, 经过`Scheduler`包的调度, 间接进行`fiber构造`
ensureRootIsScheduled(root, eventTime);
}
} else {
// 注册调度任务, 经过`Scheduler`包的调度, 间接进行`fiber构造`
// 如果同步优先级,走'Scheduler'调度
ensureRootIsScheduled(root, eventTime);
}
}
注册调度任务
与输入环节紧密相连, 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);
}
// 后半部分: 注册调度任务
//其实是在scheduleCallback去创建task,然后加入任务队列里面,后面有详细解释
let newCallbackNode;
if (newCallbackPriority === SyncLanePriority) {
newCallbackNode = scheduleSyncCallback(
performSyncWorkOnRoot.bind(null, root),
);
} else if (newCallbackPriority === SyncBatchedLanePriority) {
newCallbackNode = scheduleCallback(
ImmediateSchedulerPriority,
performSyncWorkOnRoot.bind(null, root),
);
} else {
// concurrent模式下注册任务,然后加入任务队列
const schedulerPriorityLevel = lanePriorityToSchedulerPriority(
newCallbackPriority,
);
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root),
);
}
root.callbackPriority = newCallbackPriority;
root.callbackNode = newCallbackNode;
}