从 render 阶段进入commit 阶段
重新回到 performSyncWorkOnRoot
方法中。
该方法中通过调用 workLoopSync
方法,循环构建每一个 React 元素所对应的 fiber 对象,当构建完成就会进入 commit 阶段。
实际就是下面这段代码:
// packages\react-reconciler\src\ReactFiberWorkLoop.js
// 将构建好的新 Fiber 对象存储在 finishedWork 属性中
// 提交阶段使用
root.finishedWork = (root.current.alternate: any);
root.finishedExpirationTime = expirationTime;
// 结束 render 阶段
// 进入 commit 阶段
finishSyncRender(root);
root.finishedWork
是待提交 fiber 对象,实际上就是 render 阶段的工作成果:
-
root.current
就是rootFiber
-
root.current.alternate
就是 workInProgress Fiber 树
然后调用 finishSyncRender
方法结束 render 阶段。
finishSyncRender
- 首先清空全局变量
workInProgress
,释放内存- 因为已经将它存储到
root.finishedWork
中了
- 因为已经将它存储到
- 然后真正进入 commit 阶段
function finishSyncRender(root) {
// 销毁 workInProgress Fiber 树
// 因为待提交 Fiber 对象已经被存储在了 root.finishedWork 中
workInProgressRoot = null;
// 进入 commit 阶段
commitRoot(root);
}
commitRoot
该方法的内容就是更改任务的优先级,以最高优先级执行 commit 阶段。
- 创建的任务的默认的优先级是
97
(普通优先级) - 在 commit 阶段不允许被打断(无论是初始渲染还是更新),所以要将任务的优先级设置为*
99
- 首先获取任务的优先级
renderPriorityLevel
- 然后更改任务的优先级
runWithPriority
- 实际上使用最高优先级
ImmediatePriority
去执行 commit - commit 实际执行的方法是
commitRootImpl
- 实际上使用最高优先级
- 首先获取任务的优先级
function commitRoot(root) {
// 获取任务优先级 97 => 普通优先级
const renderPriorityLevel = getCurrentPriorityLevel();
// 使用最高优先级执行当前任务, 因为 commit 阶段不可以被打断
// ImmediatePriority, 优先级为 99, 最高优先级
runWithPriority(
ImmediatePriority,
commitRootImpl.bind(null, root, renderPriorityLevel),
);
return null;
}
commit 阶段
commit 阶段实际执行的是 commitRootImpl
方法,主要关心方法中的三个 while
循环,它代表着 commit 阶段的三个子阶段。
commit 阶段可以分为三个子阶段:
- before mutation 阶段(执行 DOM 操作前)
- 执行方法
commitBeforeMutationEffects
- 调用类组件的生命周期函数
- 执行方法
- mutation 阶段(执行 DOM 操作)
- 执行方法
commitMutationEffects
- 执行方法
- layout 阶段(执行 DOM 操作后)
- 执行方法
commitLayoutEffects
- 调用类组件的生命周期函数和函数组件的钩子函数
- 执行方法
commitRootImpl
// packages\react-reconciler\src\ReactFiberWorkLoop.js
function commitRootImpl(root, renderPriorityLevel) {
/*...*/
// 获取待提交 Fiber 对象 rootFiber
const finishedWork = root.finishedWork;
/*...*/
// 如果没有任务要执行
if (finishedWork === null) {
// 阻止程序继续向下执行
return null;
}
// 重置为默认值
root.finishedWork = null;
root.finishedExpirationTime = NoWork;
/*...*/
// true
if (firstEffect !== null) {
/*...*/
// commit 第一个子阶段
nextEffect = firstEffect;
// 处理类组件的 getSnapShotBeforeUpdate 生命周期函数
do {
if (__DEV__) {
/*...*/
} else {
try {
commitBeforeMutationEffects();
} catch (error) {
invariant(nextEffect !== null, 'Should be working on an effect.');
captureCommitPhaseError(nextEffect, error);
nextEffect = nextEffect.nextEffect;
}
}
} while (nextEffect !== null);
/*...*/
// commit 第二个子阶段
nextEffect = firstEffect;
do {
if (__DEV__) {
/*...*/
} else {
try {
commitMutationEffects(root, renderPriorityLevel);
} catch (error) {
invariant(nextEffect !== null, 'Should be working on an effect.');
captureCommitPhaseError(nextEffect, error);
nextEffect = nextEffect.nextEffect;
}
}
} while (nextEffect !== null);
/*...*/
// commit 第三个子阶段
nextEffect = firstEffect;
do {
if (__DEV__) {
/*...*/
} else {
try {
commitLayoutEffects(root, expirationTime);
} catch (error) {
invariant(nextEffect !== null, 'Should be working on an effect.');
captureCommitPhaseError(nextEffect, error);
nextEffect = nextEffect.nextEffect;
}
}
} while (nextEffect !== null);
/*...*/
// 重置 nextEffect
nextEffect = null;
/*...*/
} else {/*...*/}
/*...*/
}
commit 第一个子阶段
在第一个子阶段最主要的就是调用了类组件的 getSnapshotBeforeUpdate
生命周期函数。
getSnapshotBeforeUpdate 生命周期函数只在更新阶段被执行,初始渲染时不会执行。
commitBeforeMutationEffects
// packages\react-reconciler\src\ReactFiberWorkLoop.js
// commit 阶段的第一个子阶段
// 调用类组件的 getSnapshotBeforeUpdate 生命周期函数
function commitBeforeMutationEffects() {
// 循环 effect 链
while (nextEffect !== null) {
// nextEffect 是 effect 链上从 firstEffect 到 lastEffect
// 的每一个需要commit的 fiber 对象
// 初始化渲染第一个 nextEffect 为 App 组件
// effectTag => 3
const effectTag = nextEffect.effectTag;
// console.log(effectTag);
// nextEffect = null;
// return;
// 如果 fiber 对象中里有 Snapshot 这个 effectTag 的话
// Snapshot 和更新有关系 初始化渲染 不执行
if ((effectTag & Snapshot) !== NoEffect) {
// 开发环境执行 忽略
setCurrentDebugFiberInDEV(nextEffect);
// 计 effect 的数 忽略
recordEffect();
// 获取当前 fiber 节点
const current = nextEffect.alternate;
// 当 nextEffect 上有 Snapshot 这个 effectTag 时
// 执行以下方法, 主要是类组件调用 getSnapshotBeforeUpdate 生命周期函数
commitBeforeMutationEffectOnFiber(current, nextEffect);
// 开发环境执行 忽略
resetCurrentDebugFiberInDEV();
}
// 调度 useEffect
// 初始化渲染 目前没有 不执行
// false
if ((effectTag & Passive) !== NoEffect) {
// If there are passive effects, schedule a callback to flush at
// the earliest opportunity.
if (!rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = true;
scheduleCallback(NormalPriority, () => {
// 触发useEffect
flushPassiveEffects();
return null;
});
}
}
nextEffect = nextEffect.nextEffect;
}
}
commitBeforeMutationEffectOnFiber
commitBeforeMutationLifeCycles
方法中只使用了 switch
匹配了类组件tag = ClassComponent
:
- 获取旧的 props、旧的 state 以及组件实例对象
- 然后调用实例对象的
getSnapshotBeforeUpdate
方法 - 接着将快照存储在实例对象的
__reactInternalSnapshotBeforeUpdate
属性上- 用于在执行
componentDidUpdate
生命周期函数时作为第三个参数(快照)传入
- 用于在执行
function commitBeforeMutationLifeCycles(
current: Fiber | null,
finishedWork: Fiber,
): void {
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent:
case Block: {
return;
}
// 如果该 fiber 类型是 ClassComponent
case ClassComponent: {
if (finishedWork.effectTag & Snapshot) {
if (current !== null) {
// 旧的 props
const prevProps = current.memoizedProps;
// 旧的 state
const prevState = current.memoizedState;
startPhaseTimer(finishedWork, 'getSnapshotBeforeUpdate');
// 获取 classComponent 组件的实例对象
const instance = finishedWork.stateNode;
// 执行 getSnapshotBeforeUpdate 生命周期函数
// 在组件更新前捕获一些 DOM 信息
// 返回自定义的值或 null, 统称为 snapshot
const snapshot = instance.getSnapshotBeforeUpdate(
finishedWork.elementType === finishedWork.type
? prevProps
: resolveDefaultProps(finishedWork.type, prevProps),
prevState,
);
// 将 snapshot 赋值到 __reactInternalSnapshotBeforeUpdate 属性上
instance.__reactInternalSnapshotBeforeUpdate = snapshot;
stopPhaseTimer();
}
}
return;
}
case HostRoot:
case HostComponent:
case HostText:
case HostPortal:
case IncompleteClassComponent:
// Nothing to do for these component types
return;
}
invariant(
false,
'This unit of work tag should not have side-effects. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
commit 第二个子阶段
commitMutationEffects
该阶段所做的就是根据 effectTag
属性执行 DOM 操作。
- 首先获取 fiber 节点对象的
effectTag
- 然后进行匹配
-
Placement
插入节点操作 -
PlacementAndUpdate
插入并更新 DOM -
Hydrating
服务器端渲染 -
Update
更新 DOM -
Deletion
删除 DOM
-
- 插入操作执行完将
effectTag
重置为1
,即PerformedWork
表示 DOM 操作已经执行完成
当前是初始化渲染,会调用 commitPlacement
// commit 阶段的第二个子阶段
// 根据 effectTag 执行 DOM 操作
function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {
// 循环 effect 链
while (nextEffect !== null) {
// 开发环境执行 忽略
setCurrentDebugFiberInDEV(nextEffect);
// 获取 effectTag
// 初始渲染第一次循环为 App 组件
// 即将根组件及内部所有内容一次性添加到页面中
const effectTag = nextEffect.effectTag;
// 如果有文本节点, 将 value 置为''
if (effectTag & ContentReset) {
commitResetTextContent(nextEffect);
}
// 更新 ref
if (effectTag & Ref) {
const current = nextEffect.alternate;
if (current !== null) {
commitDetachRef(current);
}
}
// 根据 effectTag 分别处理
let primaryEffectTag =
effectTag & (Placement | Update | Deletion | Hydrating);
// 匹配 effectTag
// 初始渲染 primaryEffectTag 为 2 匹配到 Placement
switch (primaryEffectTag) {
// 针对该节点及子节点进行插入操作
case Placement: {
commitPlacement(nextEffect);
// effectTag 从 3 变为 1
// 从 effect 标签中清除 "placement" 重置 effectTag 值
// 以便我们知道在调用诸如componentDidMount之类的任何生命周期之前已将其插入。
nextEffect.effectTag &= ~Placement;
break;
}
// 插入并更新 DOM
case PlacementAndUpdate: {
// 插入
commitPlacement(nextEffect);
// Clear the "placement" from effect tag so that we know that this is
// inserted, before any life-cycles like componentDidMount gets called.
nextEffect.effectTag &= ~Placement;
// 更新
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// 服务器端渲染
case Hydrating: {
nextEffect.effectTag &= ~Hydrating;
break;
}
// 服务器端渲染
case HydratingAndUpdate: {
nextEffect.effectTag &= ~Hydrating;
// Update
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// 更新 DOM
case Update: {
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// 删除 DOM
case Deletion: {
commitDeletion(root, nextEffect, renderPriorityLevel);
break;
}
}
// TODO: Only record a mutation effect if primaryEffectTag is non-zero.
recordEffect();
resetCurrentDebugFiberInDEV();
nextEffect = nextEffect.nextEffect;
}
}
commitPlacement
- 首先获取非组件级的父级 fiber 对象
- 因为组件本身不能插入 DOM 节点
- 根据父级节点的类型
parentFiber.tag
获取父级真实 DOM 节点对象- 不同类型获取的方式不同
- 获取当前节点的兄弟节点
- 根据是否有兄弟节点决定插入 DOM 的方式
- 有兄弟节点:insertBefore
- 没有兄弟节点:appendChild
- 根据是否有兄弟节点决定插入 DOM 的方式
- 判断是否是渲染容器,调用对应的方法追加节点
// packages\react-reconciler\src\ReactFiberCommitWork.js
// 挂载 DOM 元素
function commitPlacement(finishedWork: Fiber): void {
// finishedWork 初始化渲染时为根组件 Fiber 对象
if (!supportsMutation) {
return;
}
// 获取非组件父级 Fiber 对象
// 初始渲染时为 <div id="root"></div>
const parentFiber = getHostParentFiber(finishedWork);
// 存储真正的父级 DOM 节点对象
let parent;
// 是否为渲染容器
// 渲染容器和普通react元素的主要区别在于是否需要特殊处理注释节点
let isContainer;
// 获取父级 DOM 节点对象
// 但是初始渲染时 rootFiber 对象中的 stateNode 存储的是 FiberRoot
const parentStateNode = parentFiber.stateNode;
// 判断父节点的类型
// 初始渲染时是 hostRoot 3
switch (parentFiber.tag) {
case HostComponent:
parent = parentStateNode;
isContainer = false;
break;
case HostRoot:
// 获取真正的 DOM 节点对象
// <div id="root"></div>
parent = parentStateNode.containerInfo;
// 是 container 容器
isContainer = true;
break;
case HostPortal:
parent = parentStateNode.containerInfo;
isContainer = true;
break;
case FundamentalComponent:
if (enableFundamentalAPI) {
parent = parentStateNode.instance;
isContainer = false;
}
// eslint-disable-next-line-no-fallthrough
default:
invariant(
false,
'Invalid host parent fiber. This error is likely caused by a bug ' +
'in React. Please file an issue.',
);
}
// 如果父节点是文本节点的话
if (parentFiber.effectTag & ContentReset) {
// 在进行任何插入操作前, 需要先将 value 置为 ''
resetTextContent(parent);
// 清除 ContentReset 这个 effectTag
parentFiber.effectTag &= ~ContentReset;
}
// 查看当前节点是否有下一个兄弟节点
// 有, 执行 insertBefore
// 没有, 执行 appendChild
const before = getHostSibling(finishedWork);
// 渲染容器
if (isContainer) {
// 向父节点中追加节点 或者 将子节点插入到 before 节点的前面
insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
} else {
// 非渲染容器
// 向父节点中追加节点 或者 将子节点插入到 before 节点的前面
insertOrAppendPlacementNode(finishedWork, before, parent);
}
}
getHostParentFiber
// 获取 HostRootFiber 对象
function getHostParentFiber(fiber: Fiber): Fiber {
// 获取当前 Fiber 父级
let parent = fiber.return;
// 查看父级是否为 null
while (parent !== null) {
// 查看父级是否为 hostRoot
if (isHostParent(parent)) {
// 返回
return parent;
}
// 继续向上查找
parent = parent.return;
}
invariant(
false,
'Expected to find a host parent. This error is likely caused by a bug ' +
'in React. Please file an issue.',
);
}
insertOrAppendPlacementNodeIntoContainer
判断节点的类型:
- 如果是普通元素或文本,则根据
before
调用对应的方法 - 如果是组件,则获取组件的子级,再次调用
insertOrAppendPlacementNodeIntoContainer
,并处理兄弟节点
// 向容器中追加 | 插入到某一个节点的前面
function insertOrAppendPlacementNodeIntoContainer(
node: Fiber,
before: ?Instance,
parent: Container,
): void {
const {tag} = node;
// 如果待插入的节点是一个 DOM 元素或者文本的话
// 比如 组件fiber => false div => true
const isHost = tag === HostComponent || tag === HostText;
if (isHost || (enableFundamentalAPI && tag === FundamentalComponent)) {
// 获取 DOM 节点
const stateNode = isHost ? node.stateNode : node.stateNode.instance;
// 如果 before 存在
if (before) {
// 插入到 before 前面
insertInContainerBefore(parent, stateNode, before);
} else {
// 追加到父容器中
appendChildToContainer(parent, stateNode);
}
} else if (tag === HostPortal) {
// If the insertion itself is a portal, then we don't want to traverse
// down its children. Instead, we'll get insertions from each child in
// the portal directly.
} else {
// 如果是组件节点, 比如 ClassComponent, 则找它的第一个子节点(DOM 元素)
// 进行插入操作
const child = node.child;
if (child !== null) {
// 向父级中追加子节点或者将子节点插入到 before 的前面
insertOrAppendPlacementNodeIntoContainer(child, before, parent);
// 获取下一个兄弟节点
let sibling = child.sibling;
// 如果兄弟节点存在
while (sibling !== null) {
// 向父级中追加子节点或者将子节点插入到 before 的前面
insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);
// 同步兄弟节点
sibling = sibling.sibling;
}
}
}
}
insertInContainerBefore
- 判断父容器是否是注释节点
- 如果是则找到注释节点的父级
- 使用
insertBefore
插入节点
// packages\react-dom\src\client\ReactDOMHostConfig.js
export function insertInContainerBefore(
container: Container,
child: Instance | TextInstance,
beforeChild: Instance | TextInstance | SuspenseInstance,
): void {
// 如果父容器是注释节点
if (container.nodeType === COMMENT_NODE) {
// 找到注释节点的父级节点 因为注释节点没法调用 insertBefore
(container.parentNode: any).insertBefore(child, beforeChild);
} else {
// 将 child 插入到 beforeChild 的前面
container.insertBefore(child, beforeChild);
}
}
appendChildToContainer
- 判断父容器是否是注释节点
- 如果是,获取注释节点的父级,调用
insertBefore
插入节点 - 如果不是,调用
appendChild
插入节点
- 如果是,获取注释节点的父级,调用
export function appendChildToContainer(
container: Container,
child: Instance | TextInstance,
): void {
let parentNode;
// 监测 container 是否注释节点
if (container.nodeType === COMMENT_NODE) {
// 获取父级的父级
parentNode = (container.parentNode: any);
// 将子级节点插入到注释节点的前面
parentNode.insertBefore(child, container);
} else {
// 直接将 child 插入到父级中
parentNode = container;
parentNode.appendChild(child);
}
const reactRootContainer = container._reactRootContainer;
if (
(reactRootContainer === null || reactRootContainer === undefined) &&
parentNode.onclick === null
) {
// TODO: This cast may not be sound for SVG, MathML or custom elements.
trapClickOnNonInteractiveElement(((parentNode: any): HTMLElement));
}
}
commit 第三个子阶段
进入到第三个子阶段,就代表 DOM 操作已经执行完成。
该阶段要做的就是执行类组件的生命周期函数和函数组件的钩子函数(例如useEffect
)。
commitLayoutEffects
// commit 阶段的第三个子阶段
function commitLayoutEffects(
root: FiberRoot,
committedExpirationTime: ExpirationTime,
) {
while (nextEffect !== null) {
setCurrentDebugFiberInDEV(nextEffect);
// 此时 effectTag 已经被重置为 1, 表示 DOM 操作已经完成
const effectTag = nextEffect.effectTag;
// 调用生命周期函数和钩子函数
// 前提是类组件中调用了生命周期函数
// 或者函数组件中调用了 useEffect
if (effectTag & (Update | Callback)) {
recordEffect();
const current = nextEffect.alternate;
// 类组件处理生命周期函数
// 函数组件处理钩子函数
commitLayoutEffectOnFiber(
root,
current,
nextEffect,
committedExpirationTime,
);
}
// 赋值ref
// false
if (effectTag & Ref) {
recordEffect();
commitAttachRef(nextEffect);
}
resetCurrentDebugFiberInDEV();
// 更新循环条件
nextEffect = nextEffect.nextEffect;
}
}
commitLayoutEffectOnFiber
该方法通过匹配 tag
属性,判断要执行的内容,主要区分函数组件FunctionComponent
和类组件ClassComponent
:
- 类组件:
- 获取组件实例对象
- 判断如果是初始渲染阶段,调用
componentDidMount
- 如果是更新节点,调用
componentDidUpdate
并将快照instance.__reactInternalSnapshotBeforeUpdate
传递进去 - 然后获取任务队列,执行
render
方法的第三个参数,即渲染完成后要执行的回调函数
// packages\react-reconciler\src\ReactFiberCommitWork.js
function commitLifeCycles(
finishedRoot: FiberRoot,
current: Fiber | null,
finishedWork: Fiber,
committedExpirationTime: ExpirationTime,
): void {
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent:
case Block: {
// At this point layout effects have already been destroyed (during mutation phase).
// This is done to prevent sibling component effects from interfering with each other,
// e.g. a destroy function in one component should never override a ref set
// by a create function in another component during the same commit.
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
if (runAllPassiveEffectDestroysBeforeCreates) {
schedulePassiveEffects(finishedWork);
}
return;
}
case ClassComponent: {
// 获取类组件实例对象
const instance = finishedWork.stateNode;
// 如果在类组件中存在生命周期函数判断条件就会成立
if (finishedWork.effectTag & Update) {
// 初始渲染阶段
if (current === null) {
startPhaseTimer(finishedWork, 'componentDidMount');
// 调用 componentDidMount 生命周期函数
instance.componentDidMount();
stopPhaseTimer();
} else {
// 更新阶段
// 获取旧的 props
const prevProps =
finishedWork.elementType === finishedWork.type
? current.memoizedProps
: resolveDefaultProps(finishedWork.type, current.memoizedProps);
// 获取旧的 state
const prevState = current.memoizedState;
startPhaseTimer(finishedWork, 'componentDidUpdate');
// 调用 componentDidUpdate 生命周期函数
// instance.__reactInternalSnapshotBeforeUpdate 快照
// getSnapShotBeforeUpdate 方法的返回值
instance.componentDidUpdate(
prevProps,
prevState,
instance.__reactInternalSnapshotBeforeUpdate,
);
stopPhaseTimer();
}
}
// 获取任务队列
const updateQueue = finishedWork.updateQueue;
// 如果任务队列存在
if (updateQueue !== null) {
/**
* 调用 ReactElement 渲染完成之后的回调函数
* 即 render 方法的第三个参数
*/
commitUpdateQueue(finishedWork, updateQueue, instance);
}
return;
}
case HostRoot: /*...*/
case HostComponent: /*...*/
case Profiler:/*...*/
case SuspenseComponent: /*...*/
case SuspenseListComponent:
case IncompleteClassComponent:
case FundamentalComponent:
case ScopeComponent:
return;
}
invariant(
false,
'This unit of work tag should not have side-effects. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
commitUpdateQueue
// packages\react-reconciler\src\ReactUpdateQueue.js
/**
* 执行渲染完成之后的回调函数
*/
export function commitUpdateQueue<State>(
finishedWork: Fiber,
finishedQueue: UpdateQueue<State>,
instance: any,
): void {
// effects 为数组, 存储任务对象 (Update 对象)
// 但前提是在调用 render 方法时传递了回调函数, 就是 render 方法的第三个参数
// 如果没有传递, effects 就是 null
const effects = finishedQueue.effects;
// 重置 finishedQueue.effects 数组
finishedQueue.effects = null;
// 如果传递了 render 方法的第三个参数, effect 数组就不会为 null
if (effects !== null) {
// 遍历 effect 数组
for (let i = 0; i < effects.length; i++) {
// 获取数组中的第 i 个需要执行的 effect
const effect = effects[i];
// 获取 callback 回调函数
const callback = effect.callback;
// 如果回调函数不为 null
if (callback !== null) {
// 清空 effect 中的 callback
effect.callback = null;
// 执行回调函数, 并将 this 指向组件实例对象
callCallback(callback, instance);
}
}
}
}
commitHookEffectListMount 调用函数组件的钩子函数
以 useEffect
钩子函数为例,在第三个子阶段实际上是调用 useEffect
接收的第一个参数,一个回调函数。
函数组件会执行 commitHookEffectListMount
方法。
- 要执行的钩子函数都被存储到任务队列的
lastEffect
中 - 通过
lastEffect.next
获取当前要使用的 fiber 对象,并存储到next
中,next
包含几个参数:-
create
:存储useEffect
的第一个参数(回调函数) -
deps
:存储useEffect
的第二个参数 -
destroy
:存储useEffect
第一个参数返回的函数(清理函数)- 在调用
create
的结果存储到destroy
- 在组件卸载的时候调用
destroy
- 在调用
-
// packages\react-reconciler\src\ReactFiberCommitWork.js
/**
* useEffect 回调函数调用
*/
function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
// 获取任务队列
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
// 获取 lastEffect
let lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
// 如果 lastEffect 不为 null
if (lastEffect !== null) {
// 获取要执行的副作用
const firstEffect = lastEffect.next;
let effect = firstEffect;
// 通过遍历的方式调用 useEffect 中的回调函数
// 在组件中定义了调用了几次 useEffect 遍历就会执行几次
do {
if ((effect.tag & tag) === tag) {
// Mount
const create = effect.create;
// create 就是 useEffect 方法的第一个参数
// 返回值就是清理函数
effect.destroy = create();
}
// 更新循环条件
effect = effect.next;
} while (effect !== firstEffect);
}
}