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