这次是接着上一期render阶段的文章,解析挂载时render阶段的"递"阶段----beginWork函数
beginWork的调用
performUnitOfWork函数内部执行beginWork的函数
// ProfileMode 模式下,猜测和性能分析相关
if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
startProfilerTimer(unitOfWork);
next = beginWork(current, unitOfWork, subtreeRenderLanes);
stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
} else {
// 正常的流程
// 已经使用的fiber, 更新的fiber, subtreeRenderLanes: 当前的的lane
next = beginWork(current, unitOfWork, subtreeRenderLanes);
}
beginWork$1函数
处理开发环境一些问题,还有执行beginWork和捕获问题。在开发环境执行performUnitOfWork会执行beginWork$1函数。
beginWork$1 = function (current, unitOfWork, lanes) {
// 如果一个组件抛出一个错误,我们会同步重放它
// 分派事件,以便调试器将其视为未捕获
// 错误 请参阅 ReactErrorUtils 了解更多信息。
// 在进入开始阶段之前,将进行中的工作复制到虚拟对象上
// Fiber"。如果 beginWork 抛出,我们将使用它来重置状态。
// WorkInProgress的复制
var originalWorkInProgressCopy = assignFiberPropertiesInDEV(dummyFiber, unitOfWork);
try {
// 调用beginWork
return beginWork(current, unitOfWork, lanes);
} catch (originalError) {
// 捕获错误
}
};
beginWork主要函数
通过不断循环执行performUnitOfWork挂载子节点
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
const updateLanes = workInProgress.lanes;
if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (
oldProps !== newProps ||
hasLegacyContextChanged() ||
// 如果实现因热重载而改变,则强制重新渲染
(__DEV__ ? workInProgress.type !== current.type : false)
) {
// ...更新阶段的处理
} else if (!includesSomeLane(renderLanes, updateLanes)) {
//
//...
} else {
if ((current.effectTag & ForceUpdateForLegacySuspense) !== NoEffect) {
didReceiveUpdate = false;
}
}
} else {
/* 该didReceiveUpdate变量代表本次更新中本Fiber节点是否有变化 */
didReceiveUpdate = false;
}
}
mount(首屏渲染) 时会根据不同的 workInProgress.tag(组件类型)来进入到不同的子节点创建逻辑
// 找到不同类型的组件
switch (workInProgress.tag) {
case IndeterminateComponent:
// ReactDOM.render(<App />, document.getElementById("root")),挂载App组件
return mountIndeterminateComponent(
current,
workInProgress,
workInProgress.type,
renderLanes,
);
case LazyComponent:
// ...省略
case FunctionComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateFunctionComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
case ClassComponent:
// ...省略
case HostRoot:
// 第一次创建根节点时
return updateHostRoot(current, workInProgress, renderLanes);
case HostComponent:
// 创建根组件节点
return updateHostComponent(current, workInProgress, renderLanes);
case HostText:
// ...省略
// ...省略其他类型
}
创建根节点
第一循环进入的beginWork会去创建根节点Fiber,执行updateHostRoot函数
function updateHostRoot(current, workInProgress, renderLanes) {
// 压入fiber栈中和标记上下文,取html标签
// 主要使用fiberStack和valueStatck,调用push和pop函数
pushHostRootContext(workInProgress);
// 获取workInProgress的相关属性
const updateQueue = workInProgress.updateQueue;
const nextProps = workInProgress.pendingProps;
const prevState = workInProgress.memoizedState;
const prevChildren = prevState !== null ? prevState.element : null;
// 把current的更新队列赋值给workInProgress
cloneUpdateQueue(current, workInProgress);
// 更新updateQueue
processUpdateQueue(workInProgress, nextProps, null, renderLanes);
const nextState = workInProgress.memoizedState;
// Caution: React DevTools currently depends on this property
// being called "element".
const nextChildren = nextState.element;
if (nextChildren === prevChildren) {
resetHydrationState();
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
const root: FiberRoot = workInProgress.stateNode;
if (root.hydrate && enterHydrationState(workInProgress)) {
// 服务端渲染
} else {
// Otherwise reset hydration state in case we aborted and resumed another
// root.
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
resetHydrationState();
}
return workInProgress.child;
}
从函数内部可以知道
- processUpdateQueue处理更新,会把workInProgress和current的更新队列都执行一遍
- reconcileChildren->reconcileChildFibers(执行diff算法)
reconcileChildFibers
diff算法主要入口
function reconcileChildFibers(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChild: any,
lanes: Lanes,
): Fiber | null {
//
const isUnkeyedTopLevelFragment =
typeof newChild === 'object' &&
newChild !== null &&
newChild.type === REACT_FRAGMENT_TYPE &&
newChild.key === null;
if (isUnkeyedTopLevelFragment) {
newChild = newChild.props.children;
}
// Handle object types
const isObject = typeof newChild === 'object' && newChild !== null;
if (isObject) {
// 单节点diff
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:
if (enableLazyElements) {
const payload = newChild._payload;
const init = newChild._init;
// TODO: This function is supposed to be non-recursive.
return reconcileChildFibers(
returnFiber,
currentFirstChild,
init(payload),
lanes,
);
}
}
}
if (typeof newChild === 'string' || typeof newChild === 'number') {
return placeSingleChild(
reconcileSingleTextNode(
returnFiber,
currentFirstChild,
'' + newChild,
lanes,
),
);
}
// 多节点diff
if (isArray(newChild)) {
return reconcileChildrenArray(
returnFiber,
currentFirstChild,
newChild,
lanes,
);
}
if (getIteratorFn(newChild)) {
return reconcileChildrenIterator(
returnFiber,
currentFirstChild,
newChild,
lanes,
);
}
if (isObject) {
throwOnInvalidObjectType(returnFiber, newChild);
}
if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
// If the new child is undefined, and the return fiber is a composite
// component, throw an error. If Fiber return types are disabled,
// we already threw above.
switch (returnFiber.tag) {
case ClassComponent: {
if (__DEV__) {
const instance = returnFiber.stateNode;
if (instance.render._isMockFunction) {
// We allow auto-mocks to proceed as if they're returning null.
break;
}
}
}
case FunctionComponent: {
const Component = returnFiber.type;
}
}
}
// Remaining cases are all treated as empty.
return deleteRemainingChildren(returnFiber, currentFirstChild);
}
总结
- 无论挂载还是更新的流程,beginWork阶段会通过workInProgress.tag去判断节点的tag值,根据不同节点去执行不同的函数
- beginWork->reconcileChildren->根据是否存在current树调用mountChildFibers或reconcileChildFibers
- mountChildFibers或reconcileChildFibers都是根据ChildReconciler返回的函数赋值
- 下一讲将看render阶段的"归"阶段completeWork