React Fiber構(gòu)建beginWork源碼解析
引言
前文我們介紹了fiber的基本概念,以及fiber在初始化階段生成了fiberRoot和rootFiber 2個(gè)對(duì)象。
但,整個(gè)fiber樹還沒有構(gòu)建,未進(jìn)入reconciler階段。
本篇,我們將介紹,fiber鏈表的構(gòu)建之-beginWork階段
一. scheduleUpdateOnFiber
function scheduleUpdateOnFiber(fiber, lane, eventTime) { // ... if (root === workInProgressRoot) { { workInProgressRootUpdatedLanes = mergeLanes(workInProgressRootUpdatedLanes, lane); } } // ... if (lane === SyncLane) { if ( // Check if we're inside unbatchedUpdates (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering (executionContext & (RenderContext | CommitContext)) === NoContext) { // ... performSyncWorkOnRoot(root) }else { // ... ensureRootIsScheduled(root, eventTime); } }else { // ... ensureRootIsScheduled(root, eventTime); schedulePendingInteractions(root, lane); } }
fiber在內(nèi)存中,會(huì)有兩份數(shù)據(jù),一個(gè)是當(dāng)前的,一個(gè)是在內(nèi)存中正在構(gòu)建的。
這里 根據(jù)不同的啟動(dòng)模式,進(jìn)行下面的協(xié)調(diào)階段。在17版本中,一般使用sync模式。18版本默認(rèn)開啟并發(fā)模式。
二. performSyncWorkOnRoot
同步模式下的流程,如果是并發(fā)模式,會(huì)進(jìn)入schedule異步調(diào)度,最終還會(huì)執(zhí)行performSyncWorkOnRoot。
function performSyncWorkOnRoot(root) { flushPassiveEffects(); var lanes; var exitStatus; if (root === workInProgressRoot && includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)) { lanes = workInProgressRootRenderLanes; exitStatus = renderRootSync(root, lanes); if (includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes)) { lanes = getNextLanes(root, lanes); exitStatus = renderRootSync(root, lanes); } } else { lanes = getNextLanes(root, NoLanes); exitStatus = renderRootSync(root, lanes); } // ... var finishedWork = root.current.alternate; root.finishedWork = finishedWork; root.finishedLanes = lanes; commitRoot(root); ensureRootIsScheduled(root, now()); return null; }
performSyncWorkOnRoot是reconciler階段所有的執(zhí)行入口,首次渲染將進(jìn)入renderRootSync。
問題來了,為什么要先執(zhí)行flushPassiveEffects?這里留個(gè)懸念,在后續(xù)的更新流程中我們?cè)偬峒啊?/p>
關(guān)于nextLanes,這里我先拋開,先理解為render優(yōu)先級(jí),lane模型會(huì)在后續(xù)章節(jié)系統(tǒng)性的講解。
renderRootSync
function renderRootSync(root, lanes) { if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { prepareFreshStack(root, lanes); startWorkOnPendingInteractions(root, lanes); } // ... do { try { workLoopSync(); break; } catch (thrownValue) { handleError(root, thrownValue); } } while (true); // ... }
prepare階段,可以構(gòu)建雙緩存fiber,即workInProgressRoot,內(nèi)存中的fiber通過之前的createFiber方法調(diào)用,當(dāng)前fiber和內(nèi)存中fiber通過alternate相互引用。
workLoopSync即react兩大工作循環(huán)中的第一層循環(huán),從這里開始構(gòu)建fiber鏈表。
workLoopSync
function workLoopSync() { while (workInProgress !== null) { performUnitOfWork(workInProgress); } }
這里是同步構(gòu)建的情況,值得對(duì)比的是另外一個(gè)方法:
function workLoopConcurrent() { while (workInProgress !== null && !shouldYield()) { performUnitOfWork(workInProgress); } }
此方法是并發(fā)模式下的工作模式,兩者區(qū)別在于shouldYield。shouldYield由schedule調(diào)度器控制,react自己實(shí)現(xiàn)了一套瀏覽器空閑時(shí)的任務(wù)調(diào)度。 其實(shí),瀏覽器本身有對(duì)應(yīng)的api:requestIdCallback。但不同瀏覽器執(zhí)行有時(shí)間差異,不能滿足react設(shè)計(jì)需要。
performUnitOfWork
function performUnitOfWork(unitOfWork) { var current = unitOfWork.alternate; // ... var next; if ( (unitOfWork.mode & ProfileMode) !== NoMode) { startProfilerTimer(unitOfWork); next = beginWork$1(current, unitOfWork, subtreeRenderLanes); stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true); } else { next = beginWork$1(current, unitOfWork, subtreeRenderLanes); } resetCurrentFiber(); unitOfWork.memoizedProps = unitOfWork.pendingProps; if (next === null) { completeUnitOfWork(unitOfWork); } else { workInProgress = next; } ReactCurrentOwner$2.current = null; }
profile是react內(nèi)部性能跟蹤調(diào)試器,在正常的開發(fā)生產(chǎn)環(huán)境不會(huì)主動(dòng)開啟,將進(jìn)入beginWork階段
三. beginWork
function beginWork(current, workInProgress, renderLanes) { if (current !== null) { var oldProps = current.memoizedProps; var newProps = workInProgress.pendingProps; if (oldProps !== newProps || hasContextChanged() || ( // Force a re-render if the implementation changed due to hot reload: workInProgress.type !== current.type )) { didReceiveUpdate = true; } else if (!includesSomeLane(renderLanes, updateLanes)) { didReceiveUpdate = false; switch (workInProgress.tag) { // ... } } else { if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { didReceiveUpdate = true; } else { didReceiveUpdate = false; } } } else { didReceiveUpdate = false; } workInProgress.lanes = NoLanes; switch (workInProgress.tag) { case IndeterminateComponent: { return mountIndeterminateComponent(current, workInProgress, workInProgress.type, renderLanes); } case LazyComponent: { var elementType = workInProgress.elementType; return mountLazyComponent(current, workInProgress, elementType, updateLanes, renderLanes); } case FunctionComponent: { var _Component = workInProgress.type; var unresolvedProps = workInProgress.pendingProps; var resolvedProps = workInProgress.elementType === _Component ? unresolvedProps : resolveDefaultProps(_Component, unresolvedProps); return updateFunctionComponent(current, workInProgress, _Component, resolvedProps, renderLanes); } case ClassComponent: { var _Component2 = workInProgress.type; var _unresolvedProps = workInProgress.pendingProps; var _resolvedProps = workInProgress.elementType === _Component2 ? _unresolvedProps : resolveDefaultProps(_Component2, _unresolvedProps); return updateClassComponent(current, workInProgress, _Component2, _resolvedProps, renderLanes); } case HostRoot: return updateHostRoot(current, workInProgress, renderLanes); case HostComponent: return updateHostComponent(current, workInProgress, renderLanes); case HostText: return updateHostText(current, workInProgress); case SuspenseComponent: return updateSuspenseComponent(current, workInProgress, renderLanes); case HostPortal: return updatePortalComponent(current, workInProgress, renderLanes); case ForwardRef: { var type = workInProgress.type; var _unresolvedProps2 = workInProgress.pendingProps; var _resolvedProps2 = workInProgress.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2); return updateForwardRef(current, workInProgress, type, _resolvedProps2, renderLanes); } case Fragment: return updateFragment(current, workInProgress, renderLanes); case Mode: return updateMode(current, workInProgress, renderLanes); case Profiler: return updateProfiler(current, workInProgress, renderLanes); case ContextProvider: return updateContextProvider(current, workInProgress, renderLanes); case ContextConsumer: return updateContextConsumer(current, workInProgress, renderLanes); case MemoComponent: { var _type2 = workInProgress.type; var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props. var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3); { if (workInProgress.type !== workInProgress.elementType) { var outerPropTypes = _type2.propTypes; if (outerPropTypes) { checkPropTypes(outerPropTypes, _resolvedProps3, // Resolved for outer only 'prop', getComponentName(_type2)); } } } _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3); return updateMemoComponent(current, workInProgress, _type2, _resolvedProps3, updateLanes, renderLanes); } case SimpleMemoComponent: { return updateSimpleMemoComponent(current, workInProgress, workInProgress.type, workInProgress.pendingProps, updateLanes, renderLanes); } case IncompleteClassComponent: { var _Component3 = workInProgress.type; var _unresolvedProps4 = workInProgress.pendingProps; var _resolvedProps4 = workInProgress.elementType === _Component3 ? _unresolvedProps4 : resolveDefaultProps(_Component3, _unresolvedProps4); return mountIncompleteClassComponent(current, workInProgress, _Component3, _resolvedProps4, renderLanes); } case SuspenseListComponent: { return updateSuspenseListComponent(current, workInProgress, renderLanes); } case FundamentalComponent: { break; } case ScopeComponent: { break; } case Block: { { var block = workInProgress.type; var props = workInProgress.pendingProps; return updateBlock(current, workInProgress, block, props, renderLanes); } } case OffscreenComponent: { return updateOffscreenComponent(current, workInProgress, renderLanes); } case LegacyHiddenComponent: { return updateLegacyHiddenComponent(current, workInProgress, renderLanes); } } }
其中 didReceiveUpdate會(huì)在更新ref時(shí)使用到,也是props是否有變化的標(biāo)志,這里先不關(guān)注。
首次將進(jìn)入rootFiber的case,即HostRoot
updateHostRoot
function updateHostRoot(current, workInProgress, renderLanes) { // ... // 服務(wù)器端渲染處理先省略... // ... reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; }
reconcileChildren
function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { if (current === null) { // If this is a fresh new component that hasn't been rendered yet, we // won't update its child set by applying minimal side-effects. Instead, // we will add them all to the child before it gets rendered. That means // we can optimize this reconciliation pass by not tracking side-effects. workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderLanes); } else { // If the current child is the same as the work in progress, it means that // we haven't yet started any work on these children. Therefore, we use // the clone algorithm to create a copy of all the current children. // If we had any progressed work already, that is invalid at this point so // let's throw it out. workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderLanes); } }
對(duì)于首次渲染,current為Null,進(jìn)入reconcileChildFibers
reconcileChildFibers
function reconcileChildFibers(returnFiber, currentFirstChild, newChild, lanes) { var isObject = typeof newChild === 'object' && newChild !== null; if (isObject) { switch (newChild.$$typeof) { case REACT_ELEMENT_TYPE: return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes)); case REACT_PORTAL_TYPE: return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, lanes)); case REACT_LAZY_TYPE: { var payload = newChild._payload; var init = newChild._init; // TODO: This function is supposed to be non-recursive. return reconcileChildFibers(returnFiber, currentFirstChild, init(payload), lanes); } } } }
這里newChild是element對(duì)象,即render初始化階段所生成的react element。
這里,我們可以看到typeof了,除了是個(gè)symbol,防止偽造攻擊外,對(duì)于不同的類型,會(huì)有不同的fiber構(gòu)建流程。普通的typeof了,除了是個(gè)symbol,防止偽造攻擊外,對(duì)于不同的類型,會(huì)有不同的fiber構(gòu)建流程。普通的typeof了,除了是個(gè)symbol,防止偽造攻擊外,對(duì)于不同的類型,會(huì)有不同的fiber構(gòu)建流程。普通的typeof是element type。
reconcileSingleElement
function reconcileSingleElement(returnFiber, currentFirstChild, element, lanes) { var key = element.key; var child = currentFirstChild; // ... 如果存在child,遞歸刪除 // ... if (element.type === REACT_FRAGMENT_TYPE) { var created = createFiberFromFragment(element.props.children, returnFiber.mode, lanes, element.key); created.return = returnFiber; return created; } else { var _created4 = createFiberFromElement(element, returnFiber.mode, lanes); _created4.ref = coerceRef(returnFiber, currentFirstChild, element); _created4.return = returnFiber; return _created4; } }
createFiberFromElement
function createFiberFromElement(element, mode, lanes) { var owner = null; { owner = element._owner; } var type = element.type; var key = element.key; var pendingProps = element.props; var fiber = createFiberFromTypeAndProps(type, key, pendingProps, owner, mode, lanes); { fiber._debugSource = element._source; fiber._debugOwner = element._owner; } return fiber; }
根據(jù)element對(duì)象創(chuàng)建子節(jié)點(diǎn)fiber樹,并設(shè)置構(gòu)建的子fiber.return為父fiber。遍歷的方式使用的是深度優(yōu)先遍歷算法,一邊對(duì)子節(jié)點(diǎn)做fiber實(shí)例化,一邊對(duì)節(jié)點(diǎn)的上下關(guān)系做綁定
mountIndeterminateComponent
function mountIndeterminateComponent(_current, workInProgress, Component, renderLanes) { // ... prepareToReadContext(workInProgress, renderLanes); // ... setIsRendering(true); ReactCurrentOwner$1.current = workInProgress; value = renderWithHooks(null, workInProgress, Component, props, context, renderLanes); setIsRendering(false); // ... reconcileChildren(null, workInProgress, value, renderLanes); // ... }
自定義組件,將先設(shè)置rendering狀態(tài)以及全局的render fiber進(jìn)行時(shí)對(duì)象,自定義組件內(nèi)可能有副作用,比如useEffect,會(huì)影響flags。
renderWithHooks
function renderWithHooks(current, workInProgress, Component, props, secondArg, nextRenderLanes) { // ... { if (current !== null && current.memoizedState !== null) { ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV; } else if (hookTypesDev !== null) { ReactCurrentDispatcher$1.current = HooksDispatcherOnMountWithHookTypesInDEV; } else { ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV; } } var children = Component(props, secondArg); ReactCurrentDispatcher$1.current = ContextOnlyDispatcher; }
ReactCurrentDispatcher對(duì)象很重要,這是effect處理重要的全局對(duì)象,他將改變flags值,并影響后續(xù)的effect鏈表構(gòu)建。
Component即函數(shù)組件對(duì)象,執(zhí)行的結(jié)果即element對(duì)象。此element對(duì)象將再次調(diào)用reconcileChildren,進(jìn)入?yún)f(xié)調(diào)child階段,最終返回child的Fiber。
問題來了:
- fiber是一邊生成,一邊關(guān)聯(lián)關(guān)系的,那么每層的fiber是如何找到下層element的?
- 不同的fiber對(duì)象,updateQueue都一樣嗎?
- 不同的fiber對(duì)象,memoizedState都一樣嗎?
對(duì)于rootFiber而言,updateQueue掛載的element對(duì)象,經(jīng)過process update,清空updateQueue.shared,進(jìn)而將element對(duì)象掛載至memoizedState上,當(dāng)執(zhí)行reconcileChildren時(shí),nextChild從memoizedState獲取。
對(duì)于nextChild為function組件時(shí),包括頂層函數(shù)組件,將執(zhí)行renderWithHooks,返回全量的element對(duì)象,當(dāng)然renderWithHooks功能不僅僅于此,還涉及重要的flags計(jì)算。當(dāng)執(zhí)行reconcileChildren時(shí),會(huì)將element掛載至下層fiber的pendingProps上。
對(duì)于nextChild為普通節(jié)點(diǎn)時(shí),會(huì)根據(jù)層層根據(jù)pendingProps獲取下一層節(jié)點(diǎn)的信息,從而繼續(xù)構(gòu)建fiber樹。
值得注意的是,函數(shù)組件節(jié)點(diǎn)的updateQueue指的是lastEffect鏈表,他其實(shí)是一個(gè)環(huán)狀鏈表結(jié)構(gòu)。
每個(gè)節(jié)點(diǎn)的構(gòu)建,都會(huì)設(shè)置memoizedProps = pendingProps
至此,beginWork的遞歸構(gòu)建已完成,下面將進(jìn)入completeWork,更多關(guān)于React Fiber構(gòu)建beginWork的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
webpack構(gòu)建react多頁面應(yīng)用詳解
這篇文章主要介紹了webpack構(gòu)建react多頁面應(yīng)用詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09Shopee在React?Native?架構(gòu)方面的探索及發(fā)展歷程
這篇文章主要介紹了Shopee在React?Native?架構(gòu)方面的探索,本文會(huì)從發(fā)展歷史、架構(gòu)模型、系統(tǒng)設(shè)計(jì)、遷移方案四個(gè)方向逐一介紹我們?nèi)绾我徊讲降貪M足多團(tuán)隊(duì)在復(fù)雜業(yè)務(wù)中的開發(fā)需求,需要的朋友可以參考下2022-07-07TypeScript在React項(xiàng)目中的使用實(shí)踐總結(jié)
這篇文章主要介紹了TypeScript在React項(xiàng)目中的使用總結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04React hooks如何清除定時(shí)器并驗(yàn)證效果
在React中,通過自定義Hook useTimeHook實(shí)現(xiàn)定時(shí)器的啟動(dòng)與清除,在App組件中使用Clock組件展示當(dāng)前時(shí)間,利用useEffect鉤子在組件掛載時(shí)啟動(dòng)定時(shí)器,同時(shí)確保組件卸載時(shí)清除定時(shí)器,避免內(nèi)存泄露,這種方式簡(jiǎn)化了狀態(tài)管理和副作用的處理2024-10-10淺談React + Webpack 構(gòu)建打包優(yōu)化
本篇文章主要介紹了淺談React + Webpack 構(gòu)建打包優(yōu)化,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01ReactNative實(shí)現(xiàn)的橫向滑動(dòng)條效果
本文介紹了ReactNative實(shí)現(xiàn)的橫向滑動(dòng)條效果,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),補(bǔ)充介紹了ReactNative基于寬度變化實(shí)現(xiàn)的動(dòng)畫效果,感興趣的朋友跟隨小編一起看看吧2024-02-02JavaScript的React框架中的JSX語法學(xué)習(xí)入門教程
這篇文章主要介紹了JavaScript的React框架中的JSX語法學(xué)習(xí)入門教程,React是由Facebook開發(fā)并開源的高人氣js框架,需要的朋友可以參考下2016-03-03