在前面的过程中,我们已经理解了Fiber,它能帮助我们理解优化级调度机制等优化应用性能。那么接下来我们就通过Hooks和函数式组件来进一步深入React的优化。
函数式组件的处理流程
beginWork阶段
我们直接从beginWork开始,讲解最常用的函数式组件的更新和构建。源码地址
switch (workInProgress.tag) {
// 忽略代码
// 函数式组件的子节点构建
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,
);
}
// 忽略代码
在beginWork阶段,React会根据组件类型选择相应的处理方法。对于函数式组件,它会调用updateFunctionComponent方法。
更新分析
beginWork探寻阶段的主要任务是设置是否需要更新的状态。这个状态是后续许多代码判断的基础。
if (current !== null) {
// 双缓存机制
const oldProps = current.memoizedProps
const newProps = workInProgress.pendingProps
// 不相等就进入对比
if (oldProps !== newProps || hasLegacyContextChanged() || (__DEV__ ? workInProgress.type !== current.type : false)) {
// 状态标记需要更新这个状态,记住这个状态后续很多工作都与之相关
didReceiveUpdate = true
// 如果渲染车道内不包含更新就不需要更新
} else if (!includesSomeLane(renderLanes, updateLanes)) {
didReceiveUpdate = false
// 上下文特殊处理,优化
switch (workInProgress.tag) {
}
// 循坏子节点需要更新不
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)
} else {
if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
// 处理Suspense组件不会Context更新时更新的问题强制将标记变为true。
didReceiveUpdate = true
} else {
// An update was scheduled on this fiber, but there are no new props
// nor legacy context. Set this to false. If an update queue or context
// consumer produces a changed value, it will set this to true. Otherwise,
// the component will assume the children have not changed and bail out.
didReceiveUpdate = false
}
}
} else {
// 剩下的就是都不需要更新
didReceiveUpdate = false
}
这段代码主要做了以下几件事:
- 比较新旧props是否相等
- 检查context是否发生变化
- 根据比较结果设置
didReceiveUpdate状态 - 处理特殊情况,如Suspense组件的强制更新
updateFunctionComponent
在设置更新状态后,React会调用updateFunctionComponent方法来处理函数式组件。
function updateFunctionComponent(
current,
workInProgress,
Component,
nextProps: any,
renderLanes,
) {
// 忽略代码
let context;
let nextChildren;
// 这里和useContext有关,hooks分支中我们去讲一下
prepareToReadContext(workInProgress, renderLanes);
if (__DEV__) {
//忽略代码
} else {
// 这里就直接进入了hooks相关逻辑,最后返回下级ReactElement对象
nextChildren = renderWithHooks(
current,
workInProgress,
Component,
nextProps,
context,
renderLanes,
);
}
// 老规矩,如果didReceiveUpdate这个状态也就是前面提到的不需要更新,向下查找子节
if (current !== null && !didReceiveUpdate) {
bailoutHooks(current, workInProgress, renderLanes);
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
// React DevTools reads this flag.我们暂时都略过performance的东西
workInProgress.flags |= PerformedWork;
// 向下生成子fiber节点
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
updateFunctionComponent的主要工作包括:
- 准备context
- 调用
renderWithHooks执行函数组件 - 根据
didReceiveUpdate状态决定是否需要更新 - 生成子Fiber节点
renderWithHooks
renderWithHooks函数是Hooks机制的核心,它为函数式组件提供了状态管理和副作用处理的能力:
export function renderWithHooks<Props, SecondArg>(
current: Fiber | null,
workInProgress: Fiber,
Component: (p: Props, arg: SecondArg) => any,
props: Props,
secondArg: SecondArg,
nextRenderLanes: Lanes,
): any {
// 1阶段:去设置全局的状态
renderLanes = nextRenderLanes; // 当前渲染优先级
currentlyRenderingFiber = workInProgress; // 当前fiber节点, 也就是function组件对应的fiber节点
if (__DEV__) {
//忽略代码
}
// 清除当前fiber的遗留状态
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
workInProgress.lanes = NoLanes;
if (__DEV__) {
//忽略代码
} else {
// 2阶段:生成ReactElement子节点
// ReactCurrentDispatcher来自react-dom中的全局变量,确认是初始化还是更新
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
// 执行function函数
let children = Component(props, secondArg);
}
// 重置全局变量,并返回
// 执行function之后, 还原被修改的全局变量, 不影响下一次调用
renderLanes = NoLanes;
currentlyRenderingFiber = (null: any);
currentHook = null;
workInProgressHook = null;
didScheduleRenderPhaseUpdate = false;
return children;
}
这个函数的工作流程可以分为三个阶段:
- 准备阶段: 设置全局变量,清除旧的状态。
- 执行阶段: 根据是首次渲染还是更新,选择合适的Hooks处理器,然后执行组件函数。
- 清理阶段: 重置全局变量,防止影响其他组件。
执行过程分析
通过打断点分析,我们可以了解函数组件的执行过程:
初始化阶段:
- 组件首次进入
beginWork时,被标记为indeterminateComponent(不确定的组件类型) - 执行
renderWithHooks - 执行完毕后,打上
FunctionComponent标记
- 组件首次进入
更新阶段:
- 直接进入
updateFunctionComponent逻辑
- 直接进入


总结
本文详细介绍了React函数式组件的构建和更新过程,从beginWork到updateFunctionComponent,再到renderWithHooks。我们了解了React如何处理函数式组件,如何决定是否需要更新,以及Hooks机制的核心实现。
下一篇将深入探讨函数组件中具体Hooks的实现,以及是如何优化性能的。