React?Fiber構建源碼解析
引言
前面的章節(jié),我們在render方法里,簡單涉及到了fiber,那么:
- fiber究竟是什么?
- fiber的出現解決了什么問題?
- 為什么fiber之前的版本,沒辦法解決
下面,我們帶著這些問題,來仔細聊聊Fiber
一. Fiber是什么
在fiber出現之前,react的架構體系只有協調器reconciler和渲染器render。
當前有新的update時,react會遞歸所有的vdom節(jié)點,如果dom節(jié)點過多,會導致其他事件影響滯后,造成卡頓。即之前的react版本無法中斷工作過程,一旦遞歸開始無法停留下來。
為了解決這一系列問題,react歷時多年重構了底層架構,引入了fiber。 fiber的出現使得react能夠異步可中斷工作任務,并且可以在瀏覽器空閑時,從中斷處繼續(xù)往下工作。 當出現多個高優(yōu)先任務并行時,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 小部分并發(fā)模式
- 2 并發(fā)模式
啟動模式的不同,在后協調階段有具體差異。
該類引用的實例,即current對象是rootFiber。finishedWork是fiber完成協調器work之后的結果,下面有許多字段都帶有l(wèi)ane,這里可以先不關注,后面章節(jié)我們單獨聊聊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是父節(jié)點fiber,child是子節(jié)點第一個fiber,sibling是兄弟節(jié)點的fiber。
另外,flags很重要,在后續(xù)work階段會大量使用,另外flags和lane都是二進制數據對象,后面大量運用位運算。
effect對象,會在work loop階段生成,也就是副作用,比如我們寫的useEffect,都在work lopp階段被掛載。
每個fiber的stateNode指向具體實例節(jié)點。
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,我們先有個感性認知,后面章節(jié)單獨分析
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的更新隊列是后續(xù)schedule調度的task queue。
四. Root
root對象是legacyCreateRootFromDOMContainer方法的返回對象,這個對象是全局唯一,并貫穿了react后續(xù)的各階段計算。
至此,我們對應fiber有個感性的認知。另外需要說明的是,每個dom節(jié)點都是fiber,fiber通過return, child, sibling關聯其他fiber,本質上fiber是個鏈表數據結構,這一點和后續(xù)的effect數據結構還是有區(qū)別的。
在root生成后,首次初始化應用,將進入核心updateContainer方法
updateContainer(children, fiberRoot, parentComponent, callback);
updateContainer
function updateContainer(element, container, parentComponent, callback) {
// ...省略eventTime和lane相關,后續(xù)單獨介紹
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上,創(chuàng)建更新對象,并掛載至enqueueUpdate上。
update對象上payload很重要,后面在協調階段-fiber樹構建階段是重要的輸入。
update對象也是鏈表結構,通過next關聯下一個update對象。
至此,fiber的初始化對象到這里就結束了,目前內存中只存在fiberRoot和rootFiber對象,下面調的scheduleUpdateOnFiber方法,將正式進入協調階段,更多關于React構建Fiber的資料請關注腳本之家其它相關文章!
相關文章
react-router v4如何使用history控制路由跳轉詳解
這篇文章主要給大家介紹了關于react-router v4如何使用history控制路由跳轉的相關資料,文中通過示例代碼介紹的的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。2018-01-01
如何應用?SOLID?原則在?React?中整理代碼之開閉原則
React?不是面向對象,但這些原則背后的主要思想可能是有幫助的,在本文中,我將嘗試演示如何應用這些原則來編寫更好的代碼,對React?SOLID原則開閉原則相關知識感興趣的朋友一起看看吧2022-07-07
React-native橋接Android原生開發(fā)詳解
本篇文章主要介紹了React-native橋接Android原生開發(fā)詳解,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01

