Appearance
beginWork(FiberNode 更新入口)
这个在 React 的源码中是一个单独的文件 react-reconciler/src/ReactFiberBeginWork.new.js
这个阶段的主要特点是:
- 产生 current (没有 current 的说明是初次渲染,然后在 beginWork 的流程通过 workInProgree 生成 FiberNode,并存在 alternate 上,作为下次的 current )
- 根据 lanes 和 childLanes 生成 FiberNode
其主要作用是:根据组件类型的不同生成对应的 FiberNode 对象,并在 update 的过程判断原 FiberNode 是否可以复用
主要流程
判断节点是否可以复用 (didReceiveUpdate)
根据不同的节点类型去调用对应的 渲染方法
- IndeterminateComponent => mountIndeterminateComponent
- LazyComponent => mountLazyComponent
- FunctionComponent => updateFunctionComponent
- ClassComponent => updateClassComponent
- HostRoot => updateHostRoot
- HostComponent => updateHostComponent
- HostText => updateHostText
- SuspenseComponent => updateSuspenseComponent
- HostPortal => updatePortalComponent
- ForwardRef => updateForwardRef
- Fragment => updateFragment
- ContextProvider => updateContextProvider
- MemoComponent => updateMemoComponent
- SimpleMemoComponent => updateSimpleMemoComponent
FunctionComponent 类型的
- renderWithHooks
- reconcileChildren
- mountChildFibers | reconcileChildFibers
FiberNode 复用和强制更新
我们看一下判断条件
js
// 如果 current 存在,那么说明这个FiberNode节点是更新流程
if (current !== null) {
// 获取老的 props
const oldProps = current.memoizedProps;
// 获取新的 props
const newProps = workInProgress.pendingProps;
// 如果
// 1. 新老 props 不相等, 这边是对象的地址比较
// 2. context 改变了
// 3. 新旧组件的类型不同
if (
oldProps !== newProps ||
hasLegacyContextChanged() ||
// Force a re-render if the implementation changed due to hot reload:
(__DEV__ ? workInProgress.type !== current.type : false)
) {
// If props or context changed, mark the fiber as having performed work.
// This may be unset if the props are determined to be equal later (memo).
didReceiveUpdate = true;
} else {
// Neither props nor legacy context changes. Check if there's a pending
// update or context change.
const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
current,
renderLanes
);
// 2. 如果当前FiberNode节点不存在待更新的任务,也不存在上下文更新,那么就可以强制不更新了
if (
!hasScheduledUpdateOrContext &&
// If this is the second pass of an error or suspense boundary, there
// may not be work scheduled on `current`, so we check for this flag.
// 3. 如果存在 DidCapture 标记,那么说明这个节点存在 错误边界 或者 suspense 边界,那么也不能复用
(workInProgress.flags & DidCapture) === NoFlags
) {
// No pending updates or context. Bail out now.
// 不需要更新
didReceiveUpdate = false;
return attemptEarlyBailoutIfNoScheduledUpdate(
current,
workInProgress,
renderLanes
);
}
if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
// This is a special case that only exists for legacy mode.
// See https://github.com/facebook/react/pull/19216.
didReceiveUpdate = true;
} else {
didReceiveUpdate = false;
}
}
} else {
didReceiveUpdate = false;
if (getIsHydrating() && isForkedChild(workInProgress)) {
// Check if this child belongs to a list of muliple children in
// its parent.
//
// In a true multi-threaded implementation, we would render children on
// parallel threads. This would represent the beginning of a new render
// thread for this subtree.
//
// We only use this for id generation during hydration, which is why the
// logic is located in this special branch.
const slotIndex = workInProgress.index;
const numberOfForks = getForksAtLevel(workInProgress);
pushTreeId(workInProgress, numberOfForks, slotIndex);
}
}从上面的代码我们可以看到对于组件的复用的条件有以下几种
- 如果 新老 props 不相等 或者 context 改变了 或者 新旧组件的类型不同,那么就可以复用了
- 不存在待更新的任务,也不存在上下文更新,那么就可以强制不更新了
- 存在 DidCapture 标记,那么说明这个节点存在 错误边界 或者 suspense 边界,那么也不能复用
- 存在 ForceUpdateForLegacySuspense 标记,那么说明这个节点存在 错误边界 或者 suspense 边界,那么也不能复用
问题
- 在第一次判断 hasLegacyContextChanged() 是否改变后,又判断了 hasScheduledUpdateOrContext 是否改变,这是为什么?
