React源码Part7——Commit(Mutation阶段)
Posted 冷咖啡
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React源码Part7——Commit(Mutation阶段)相关的知识,希望对你有一定的参考价值。
React源码Part-1——代数效应:https://segmentfault.com/a/11...
React源码Part2——渲染原理: https://segmentfault.com/a/11...
React源码Part3——Fiber架构:https://segmentfault.com/a/11...
React源码Part4——Render渲染(Mount阶段):https://segmentfault.com/a/11...
React源码Part4——Render渲染(Update阶段):https://segmentfault.com/a/11...
React源码Part5——commit阶段(处理class组件生命周期): https://segmentfault.com/a/11...
React源码Part6——Commit阶段(beforeMutation):https://segmentfault.com/a/11...
React源码Part8——Commit(Layout阶段):https://segmentfault.com/a/11...
参考链接:React技术揭秘——https://react.iamkasong.com/p...
Mutation阶段主要做了什么
1.入口函数:commitMutationEffects()
2.准备开始执行:commitMutationEffects_begin
3.遍历Effect链表:commitMutationEffects_complete
4.对得到的单Fibre节点进行处理:commitMutationEffectsOnFiber
5.对于Class组件而言,会在这个阶段执行componentWillUnMount生命周期;对于函数组件Hook而言,会执行从useEffect的返回值,用于清理某些副作用。
- 对于下面这个useEffect,会在Layout阶段执行handleStatusChange函数;在Mutation阶段执行useEffect返回的函数,用于清理副作用
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
commitMutationEffects
- Mutation阶段的入口函数
export function commitMutationEffects(
root: FiberRoot,
firstChild: Fiber,
committedLanes: Lanes,
) {
inProgressLanes = committedLanes;
inProgressRoot = root;
nextEffect = firstChild;
commitMutationEffects_begin(root);
inProgressLanes = null;
inProgressRoot = null;
}
commitMutationEffects_begin——开始进入Mutation阶段
- 对Effect链表遍历,且如果Fibre节点上有需要删除的child就删
commitDeletion(root, childToDelete, fiber)
- 传入单个Fiber节点给
commitMutationEffects_complete
处理
function commitMutationEffects_begin(root: FiberRoot) {
while (nextEffect !== null) {
const fiber = nextEffect;
// TODO: Should wrap this in flags check, too, as optimization
const deletions = fiber.deletions;
if (deletions !== null) {
for (let i = 0; i < deletions.length; i++) {
const childToDelete = deletions[i];
if (__DEV__) {
// DEV 环境的东西可以暂时不管
} else {
try {
commitDeletion(root, childToDelete, fiber);
} catch (error) {
captureCommitPhaseError(childToDelete, fiber, error);
}
}
}
}
const child = fiber.child;
if ((fiber.subtreeFlags & MutationMask) !== NoFlags && child !== null) {
ensureCorrectReturnPointer(child, fiber);
nextEffect = child;
} else {
commitMutationEffects_complete(root);
}
}
}
commitUnmount——删除Fibre节点
- commitDeletion函数最终会执行 commitUnmount函数对该Fibre 节点销毁。对不同类型的Fibre节点执行不同的操作,对于class组件而言,也是在这个函数中调用
componentWillUnMount()
生命周期函数
function commitUnmount(
finishedRoot: FiberRoot,
current: Fiber,
nearestMountedAncestor: Fiber,
): void {
onCommitUnmount(current);
switch (current.tag) {
case FunctionComponent:
case ForwardRef:
case MemoComponent:
case SimpleMemoComponent: {
const updateQueue: FunctionComponentUpdateQueue | null = (current.updateQueue: any);
if (updateQueue !== null) {
const lastEffect = updateQueue.lastEffect;
if (lastEffect !== null) {
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
const {destroy, tag} = effect;
if (destroy !== undefined) {
if ((tag & HookLayout) !== NoHookEffect) {
if (
enableProfilerTimer &&
enableProfilerCommitHooks &&
current.mode & ProfileMode
) {
startLayoutEffectTimer();
safelyCallDestroy(current, nearestMountedAncestor, destroy);
recordLayoutEffectDuration(current);
} else {
safelyCallDestroy(current, nearestMountedAncestor, destroy);
}
}
}
effect = effect.next;
} while (effect !== firstEffect);
}
}
return;
}
// class组件,在销毁某个Fibre节点时,会调用它的 componentWillUnmount 函数
case ClassComponent: {
safelyDetachRef(current, nearestMountedAncestor);
const instance = current.stateNode;
if (typeof instance.componentWillUnmount === \'function\') {
safelyCallComponentWillUnmount(
current,
nearestMountedAncestor,
instance,
);
}
return;
}
case HostComponent: {
safelyDetachRef(current, nearestMountedAncestor);
return;
}
case HostPortal: {
// TODO: this is recursive.
// We are also not using this parent because
// the portal will get pushed immediately.
if (supportsMutation) {
unmountHostComponents(finishedRoot, current, nearestMountedAncestor);
} else if (supportsPersistence) {
emptyPortalContainer(current);
}
return;
}
case DehydratedFragment: {
if (enableSuspenseCallback) {
const hydrationCallbacks = finishedRoot.hydrationCallbacks;
if (hydrationCallbacks !== null) {
const onDeleted = hydrationCallbacks.onDeleted;
if (onDeleted) {
onDeleted((current.stateNode: SuspenseInstance));
}
}
}
return;
}
case ScopeComponent: {
if (enableScopeAPI) {
safelyDetachRef(current, nearestMountedAncestor);
}
return;
}
}
}
commitMutationEffects_complete——对单个Fibre节点预处理
- 处理传进来的Fibre节点,其nextEffect有值表示需要对该Fiber进行操作,传入
commitMutationEffectsOnFiber
处理 - 如果nextEffect没有值,对其Fibre节点进行遍历和调度,
ensureCorrectReturnPointer
function commitMutationEffects_complete(root: FiberRoot) {
while (nextEffect !== null) {
const fiber = nextEffect;
if (__DEV__) {
// DEV 环境的东西可以暂时不管
} else {
try {
commitMutationEffectsOnFiber(fiber, root);
} catch (error) {
captureCommitPhaseError(fiber, fiber.return, error);
}
}
const sibling = fiber.sibling;
if (sibling !== null) {
ensureCorrectReturnPointer(sibling, fiber.return);
nextEffect = sibling;
return;
}
nextEffect = fiber.return;
}
}
commitMutationEffectsOnFiber——根据不同类型的Fiber节点执行不同操作
- 对需要操作的Fibre节点进行操作,有ref相关操作的就执行ref相关操作,再根据不同的flags值做不同的DOM操作
- commitPlacement: 表示处理DOM的插入
- commitWork:表示对DOM的属性更新
function commitMutationEffectsOnFiber(finishedWork: Fiber, root: FiberRoot) {
const flags = finishedWork.flags;
// 是否需要重置文本内容
if (flags & ContentReset) {
commitResetTextContent(finishedWork);
}
// 处理 ref 相关的操作
if (flags & Ref) {
const current = finishedWork.alternate;
if (current !== null) {
commitDetachRef(current);
}
if (enableScopeAPI) {
// TODO: This is a temporary solution that allowed us to transition away
// from React Flare on www.
if (finishedWork.tag === ScopeComponent) {
commitAttachRef(finishedWork);
}
}
}
// 根据不同的 flags值执行不同的操作
const primaryFlags = flags & (Placement | Update | Hydrating);
outer: switch (primaryFlags) {
case Placement: { // 插入新的DOM
commitPlacement(finishedWork);
finishedWork.flags &= ~Placement;
break;
}
case PlacementAndUpdate: { // 更新DOM属性和插入DOM
// Placement
commitPlacement(finishedWork);
finishedWork.flags &= ~Placement;
// Update
const current = finishedWork.alternate;
commitWork(current, finishedWork);
break;
}
case Hydrating: { // SSR 环境下的插入DOM
finishedWork.flags &= ~Hydrating;
break;
}
case HydratingAndUpdate: { // SSR 环境下的插入DOM和更新DOM
finishedWork.flags &= ~Hydrating;
// Update
const current = finishedWork.alternate;
commitWork(current, finishedWork);
break;
}
case Update: { // 更新DOM属性
const current = finishedWork.alternate;
commitWork(current, finishedWork);
break;
}
}
}
以上是关于React源码Part7——Commit(Mutation阶段)的主要内容,如果未能解决你的问题,请参考以下文章