React Fiber構建beginWork源碼解析
引言
前文我們介紹了fiber的基本概念,以及fiber在初始化階段生成了fiberRoot和rootFiber 2個對象。
但,整個fiber樹還沒有構建,未進入reconciler階段。
本篇,我們將介紹,fiber鏈表的構建之-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在內存中,會有兩份數據,一個是當前的,一個是在內存中正在構建的。
這里 根據不同的啟動模式,進行下面的協(xié)調階段。在17版本中,一般使用sync模式。18版本默認開啟并發(fā)模式。
二. performSyncWorkOnRoot
同步模式下的流程,如果是并發(fā)模式,會進入schedule異步調度,最終還會執(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í)行入口,首次渲染將進入renderRootSync。
問題來了,為什么要先執(zhí)行flushPassiveEffects?這里留個懸念,在后續(xù)的更新流程中我們再提及。
關于nextLanes,這里我先拋開,先理解為render優(yōu)先級,lane模型會在后續(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階段,可以構建雙緩存fiber,即workInProgressRoot,內存中的fiber通過之前的createFiber方法調用,當前fiber和內存中fiber通過alternate相互引用。
workLoopSync即react兩大工作循環(huán)中的第一層循環(huán),從這里開始構建fiber鏈表。
workLoopSync
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
這里是同步構建的情況,值得對比的是另外一個方法:
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
此方法是并發(fā)模式下的工作模式,兩者區(qū)別在于shouldYield。shouldYield由schedule調度器控制,react自己實現(xiàn)了一套瀏覽器空閑時的任務調度。 其實,瀏覽器本身有對應的api:requestIdCallback。但不同瀏覽器執(zhí)行有時間差異,不能滿足react設計需要。
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內部性能跟蹤調試器,在正常的開發(fā)生產環(huá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會在更新ref時使用到,也是props是否有變化的標志,這里先不關注。
首次將進入rootFiber的case,即HostRoot
updateHostRoot
function updateHostRoot(current, workInProgress, renderLanes) {
// ...
// 服務器端渲染處理先省略...
// ...
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);
}
}
對于首次渲染,current為Null,進入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對象,即render初始化階段所生成的react element。
這里,我們可以看到typeof了,除了是個symbol,防止偽造攻擊外,對于不同的類型,會有不同的fiber構建流程。普通的typeof了,除了是個symbol,防止偽造攻擊外,對于不同的類型,會有不同的fiber構建流程。普通的typeof了,除了是個symbol,防止偽造攻擊外,對于不同的類型,會有不同的fiber構建流程。普通的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;
}
根據element對象創(chuàng)建子節(jié)點fiber樹,并設置構建的子fiber.return為父fiber。遍歷的方式使用的是深度優(yōu)先遍歷算法,一邊對子節(jié)點做fiber實例化,一邊對節(jié)點的上下關系做綁定
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);
// ...
}
自定義組件,將先設置rendering狀態(tài)以及全局的render fiber進行時對象,自定義組件內可能有副作用,比如useEffect,會影響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對象很重要,這是effect處理重要的全局對象,他將改變flags值,并影響后續(xù)的effect鏈表構建。
Component即函數組件對象,執(zhí)行的結果即element對象。此element對象將再次調用reconcileChildren,進入協(xié)調child階段,最終返回child的Fiber。
問題來了:
- fiber是一邊生成,一邊關聯(lián)關系的,那么每層的fiber是如何找到下層element的?
- 不同的fiber對象,updateQueue都一樣嗎?
- 不同的fiber對象,memoizedState都一樣嗎?
對于rootFiber而言,updateQueue掛載的element對象,經過process update,清空updateQueue.shared,進而將element對象掛載至memoizedState上,當執(zhí)行reconcileChildren時,nextChild從memoizedState獲取。
對于nextChild為function組件時,包括頂層函數組件,將執(zhí)行renderWithHooks,返回全量的element對象,當然renderWithHooks功能不僅僅于此,還涉及重要的flags計算。當執(zhí)行reconcileChildren時,會將element掛載至下層fiber的pendingProps上。
對于nextChild為普通節(jié)點時,會根據層層根據pendingProps獲取下一層節(jié)點的信息,從而繼續(xù)構建fiber樹。
值得注意的是,函數組件節(jié)點的updateQueue指的是lastEffect鏈表,他其實是一個環(huán)狀鏈表結構。
每個節(jié)點的構建,都會設置memoizedProps = pendingProps
至此,beginWork的遞歸構建已完成,下面將進入completeWork,更多關于React Fiber構建beginWork的資料請關注腳本之家其它相關文章!
相關文章
React創(chuàng)建組件的三種方式及其區(qū)別是什么
在React中,創(chuàng)建組件的三種主要方式是函數式組件、類組件和使用React Hooks的函數式組件,本文就詳細的介紹一下如何使用,感興趣的可以了解一下2023-08-08
再次談論React.js實現(xiàn)原生js拖拽效果引起的一系列問題
React 起源于 Facebook 的內部項目,因為該公司對市場上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設 Instagram 的網站.本文給大家介紹React.js實現(xiàn)原生js拖拽效果,需要的朋友一起學習吧2016-04-04

