引言
前文我们介绍了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在内存中,会有两份数据,一个是当前的,一个是在内存中正在构建的。
这里 根据不同的启动模式,进行下面的协调阶段。在17版本中,一般使用sync模式。18版本默认开启并发模式。
二. performSyncWorkOnRoot
同步模式下的流程,如果是并发模式,会进入schedule异步调度,最终还会执行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阶段所有的执行入口,首次渲染将进入renderRootSync。
问题来了,为什么要先执行flushPassiveEffects?这里留个悬念,在后续的更新流程中我们再提及。
关于nextLanes,这里我先抛开,先理解为render优先级,lane模型会在后续章节系统性的讲解。
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两大工作循环中的第一层循环,从这里开始构建fiber链表。
workLoopSync
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
这里是同步构建的情况,值得对比的是另外一个方法:
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
此方法是并发模式下的工作模式,两者区别在于shouldYield。shouldYield由schedule调度器控制,react自己实现了一套浏览器空闲时的任务调度。 其实,浏览器本身有对应的api:requestIdCallback。但不同浏览器执行有时间差异,不能满足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内部性能跟踪调试器,在正常的开发生产环境不会主动开启,将进入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对象创建子节点fiber树,并设置构建的子fiber.return为父fiber。遍历的方式使用的是深度优先遍历算法,一边对子节点做fiber实例化,一边对节点的上下关系做绑定
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状态以及全局的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值,并影响后续的effect链表构建。
Component即函数组件对象,执行的结果即element对象。此element对象将再次调用reconcileChildren,进入协调child阶段,最终返回child的Fiber。
问题来了:
- fiber是一边生成,一边关联关系的,那么每层的fiber是如何找到下层element的?
- 不同的fiber对象,updateQueue都一样吗?
- 不同的fiber对象,memoizedState都一样吗?
对于rootFiber而言,updateQueue挂载的element对象,经过process update,清空updateQueue.shared,进而将element对象挂载至memoizedState上,当执行reconcileChildren时,nextChild从memoizedState获取。
对于nextChild为function组件时,包括顶层函数组件,将执行renderWithHooks,返回全量的element对象,当然renderWithHooks功能不仅仅于此,还涉及重要的flags计算。当执行reconcileChildren时,会将element挂载至下层fiber的pendingProps上。
对于nextChild为普通节点时,会根据层层根据pendingProps获取下一层节点的信息,从而继续构建fiber树。
值得注意的是,函数组件节点的updateQueue指的是lastEffect链表,他其实是一个环状链表结构。
每个节点的构建,都会设置memoizedProps = pendingProps
至此,beginWork的递归构建已完成,下面将进入completeWork,更多关于React Fiber构建beginWork的资料请关注编程网其它相关文章!