ReactFiber构建源码解析
引言
前面的章节,我们在render方法里,简单涉及到了fiber,那么:
- fiber究竟是什么?
- fiber的出现解决了什么问题?
- 为什么fiber之前的版本,没办法解决
下面,我们带着这些问题,来仔细聊聊Fiber
一. Fiber是什么
在fiber出现之前,react的架构体系只有协调器reconciler和渲染器render。
当前有新的update时,react会递归所有的vdom节点,如果dom节点过多,会导致其他事件影响滞后,造成卡顿。即之前的react版本无法中断工作过程,一旦递归开始无法停留下来。
为了解决这一系列问题,react历时多年重构了底层架构,引入了fiber。 fiber的出现使得react能够异步可中断工作任务,并且可以在浏览器空闲时,从中断处继续往下工作。 当出现多个高优先任务并行时,react引入lane模型取代之前的expireTime机制。
这里提及下vue工作原理,为什么有新的update任务时,vue不需要做全量递归,而react需要?(留个悬念,大家可以先思考下)
fiber本质上是一种数据结构,在react17后,没有vdom概念,一切皆是Fiber,但Fiber != vdom。
二. FiberRoot
FiberRoot是react启动阶段,要构建的fiber对象。与之容易混淆是rootFiber,下面会具体介绍。
fiberRoot生成
function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {
var root = new FiberRootNode(containerInfo, tag, hydrate);
// stateNode is any.
var uninitializedFiber = createHostRootFiber(tag);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
initializeUpdateQueue(uninitializedFiber);
return root;
}
fiberRoot类
function FiberRootNode(containerInfo, tag, hydrate) {
this.tag = tag;
this.containerInfo = containerInfo;
this.pendingChildren = null;
this.current = null;
this.pingCache = null;
this.finishedWork = null;
this.timeoutHandle = noTimeout;
this.context = null;
this.pendingContext = null;
this.hydrate = hydrate;
this.callbackNode = null;
this.callbackPriority = NoLanePriority;
this.eventTimes = createLaneMap(NoLanes);
this.expirationTimes = createLaneMap(NoTimestamp);
this.pendingLanes = NoLanes;
this.suspendedLanes = NoLanes;
this.pingedLanes = NoLanes;
this.expiredLanes = NoLanes;
this.mutableReadLanes = NoLanes;
this.finishedLanes = NoLanes;
this.entangledLanes = NoLanes;
this.entanglements = createLaneMap(NoLanes);
{
this.mutableSourceEagerHydrationData = null;
}
{
this.interactionThreadID = unstable_getThreadID();
this.memoizedInteractions = new Set();
this.pendingInteractionMap = new Map();
}
{
switch (tag) {
case BlockingRoot:
this._debugRootType = 'createBlockingRoot()';
break;
case ConcurrentRoot:
this._debugRootType = 'createRoot()';
break;
case LegacyRoot:
this._debugRootType = 'createLegacyRoot()';
break;
}
}
}
fiberRoot本质上fiber的顶层对象,其中tag记录了几种启动模式:
- 0普通模式
- 1 小部分并发模式
- 2 并发模式
启动模式的不同,在后协调阶段有具体差异。
该类引用的实例,即current对象是rootFiber。finishedWork是fiber完成协调器work之后的结果,下面有许多字段都带有lane,这里可以先不关注,后面章节我们单独聊聊Lane模型
三. RootFiber
rootFiber生成
function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {
var root = new FiberRootNode(containerInfo, tag, hydrate);
// stateNode is any.
var uninitializedFiber = createHostRootFiber(tag);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
initializeUpdateQueue(uninitializedFiber);
return root;
}
createHostRootFiber
function createHostRootFiber(tag) {
var mode;
if (tag === ConcurrentRoot) {
mode = ConcurrentMode | BlockingMode | StrictMode;
} else if (tag === BlockingRoot) {
mode = BlockingMode | StrictMode;
} else {
mode = NoMode;
}
if ( isDevToolsPresent) {
// Always collect profile timings when DevTools are present.
// This enables DevTools to start capturing timing at any point–
// Without some nodes in the tree having empty base times.
mode |= ProfileMode;
}
return createFiber(HostRoot, null, null, mode);
}
FiberNode
function FiberNode(tag, pendingProps, key, mode) {
this.tag = tag;
this.key = key;
this.elementType = null;
this.type = null;
this.stateNode = null;
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
this.ref = null;
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode; // Effects
this.flags = NoFlags;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null;
{
this.actualDuration = Number.NaN;
this.actualStartTime = Number.NaN;
this.selfBaseDuration = Number.NaN;
this.treeBaseDuration = Number.NaN; // It's okay to replace the initial doubles with smis after initialization.
this.actualDuration = 0;
this.actualStartTime = -1;
this.selfBaseDuration = 0;
this.treeBaseDuration = 0;
}
{
// This isn't directly used but is handy for debugging internals:
this._debugID = debugCounter++;
this._debugSource = null;
this._debugOwner = null;
this._debugNeedsRemount = false;
this._debugHookTypes = null;
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
Object.preventExtensions(this);
}
}
}
到这里,fiberNode是一个类,往下的所有dom都将实例化这个类。
这里的tag依旧是启动模式,return是父节点fiber,child是子节点第一个fiber,sibling是兄弟节点的fiber。
另外,flags很重要,在后续work阶段会大量使用,另外flags和lane都是二进制数据对象,后面大量运用位运算。
effect对象,会在work loop阶段生成,也就是副作用,比如我们写的useEffect,都在work lopp阶段被挂载。
每个fiber的stateNode指向具体实例节点。
flags
export type Flags = number;
export const NoFlags = 0b000000000000000000;
export const PerformedWork = 0b000000000000000001;
export const Placement = 0b000000000000000010;
export const Update = 0b000000000000000100;
export const PlacementAndUpdate = 0b000000000000000110;
export const Deletion = 0b000000000000001000;
export const ContentReset = 0b000000000000010000;
export const Callback = 0b000000000000100000;
export const DidCapture = 0b000000000001000000;
export const Ref = 0b000000000010000000;
export const Snapshot = 0b000000000100000000;
export const Passive = 0b000000001000000000;
// TODO (effects) Remove this bit once the new reconciler is synced to the old.
export const PassiveUnmountPendingDev = 0b000010000000000000;
export const Hydrating = 0b000000010000000000;
export const HydratingAndUpdate = 0b000000010000000100;
// Passive & Update & Callback & Ref & Snapshot
export const LifecycleEffectMask = 0b000000001110100100;
// Union of all host effects
export const HostEffectMask = 0b000000011111111111;
// These are not really side effects, but we still reuse this field.
export const Incomplete = 0b000000100000000000;
export const ShouldCapture = 0b000001000000000000;
export const ForceUpdateForLegacySuspense = 0b000100000000000000;
export const PassiveStatic = 0b001000000000000000;
export const BeforeMutationMask = 0b000000001100001010;
export const MutationMask = 0b000000010010011110;
export const LayoutMask = 0b000000000010100100;
export const PassiveMask = 0b000000001000001000;
export const StaticMask = 0b001000000000000000;
export const MountLayoutDev = 0b010000000000000000;
export const MountPassiveDev = 0b100000000000000000;
lane
export const NoLanes: Lanes = 0b0000000000000000000000000000000;
export const NoLane: Lane = 0b0000000000000000000000000000000;
export const SyncLane: Lane = 0b0000000000000000000000000000001;
export const SyncBatchedLane: Lane = 0b0000000000000000000000000000010;
export const InputDiscreteHydrationLane: Lane = 0b0000000000000000000000000000100;
const InputDiscreteLanes: Lanes = 0b0000000000000000000000000011000;
const InputContinuousHydrationLane: Lane = 0b0000000000000000000000000100000;
const InputContinuousLanes: Lanes = 0b0000000000000000000000011000000;
export const DefaultHydrationLane: Lane = 0b0000000000000000000000100000000;
export const DefaultLanes: Lanes = 0b0000000000000000000111000000000;
const TransitionHydrationLane: Lane = 0b0000000000000000001000000000000;
const TransitionLanes: Lanes = 0b0000000001111111110000000000000;
const RetryLanes: Lanes = 0b0000011110000000000000000000000;
export const SomeRetryLane: Lanes = 0b0000010000000000000000000000000;
export const SelectiveHydrationLane: Lane = 0b0000100000000000000000000000000;
const NonIdleLanes = 0b0000111111111111111111111111111;
export const IdleHydrationLane: Lane = 0b0001000000000000000000000000000;
const IdleLanes: Lanes = 0b0110000000000000000000000000000;
export const OffscreenLane: Lane = 0b1000000000000000000000000000000;
关于flags和lane,我们先有个感性认知,后面章节单独分析
initializeUpdateQueue
function initializeUpdateQueue(fiber) {
var queue = {
baseState: fiber.memoizedState,
firstBaseUpdate: null,
lastBaseUpdate: null,
shared: {
pending: null
},
effects: null
};
fiber.updateQueue = queue;
}
rootFiber上调用initializeUpdateQueue,初始化queue对象,这里仅仅是初始化对象而已,并不是许多文章说fiber加入更新队列。fiber的更新队列和这里没有任何关系,fiber的更新队列是后续schedule调度的task queue。
四. Root
root对象是legacyCreateRootFromDOMContainer方法的返回对象,这个对象是全局唯一,并贯穿了react后续的各阶段计算。
至此,我们对应fiber有个感性的认知。另外需要说明的是,每个dom节点都是fiber,fiber通过return, child, sibling关联其他fiber,本质上fiber是个链表数据结构,这一点和后续的effect数据结构还是有区别的。
在root生成后,首次初始化应用,将进入核心updateContainer方法
updateContainer(children, fiberRoot, parentComponent, callback);
updateContainer
function updateContainer(element, container, parentComponent, callback) {
// ...省略eventTime和lane相关,后续单独介绍
var update = createUpdate(eventTime, lane); // Caution: React DevTools currently depends on this property
// being called "element".
update.payload = {
element: element
};
callback = callback === undefined ? null : callback;
if (callback !== null) {
{
if (typeof callback !== 'function') {
error('render(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callback);
}
}
update.callback = callback;
}
enqueueUpdate(current$1, update);
scheduleUpdateOnFiber(current$1, lane, eventTime);
return lane;
}
createUpdate
function createUpdate(eventTime, lane) {
var update = {
eventTime: eventTime,
lane: lane,
tag: UpdateState,
payload: null,
callback: null,
next: null
};
return update;
}
function enqueueUpdate(fiber, update) {
var updateQueue = fiber.updateQueue;
if (updateQueue === null) {
// Only occurs if the fiber has been unmounted.
return;
}
var sharedQueue = updateQueue.shared;
var 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;
}
在rootFiber上,创建更新对象,并挂载至enqueueUpdate上。
update对象上payload很重要,后面在协调阶段-fiber树构建阶段是重要的输入。
update对象也是链表结构,通过next关联下一个update对象。
至此,fiber的初始化对象到这里就结束了,目前内存中只存在fiberRoot和rootFiber对象,下面调的scheduleUpdateOnFiber方法,将正式进入协调阶段,更多关于React构建Fiber的资料请关注编程网其它相关文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341