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的返回值,用于清理某些副作用。

  1. 对于下面这个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阶段)的主要内容,如果未能解决你的问题,请参考以下文章

react源码学习-架构篇-commit阶段

react源码debugger-commit阶段的完成

react源码debugger-commit阶段的完成

自顶而下学习react源码 架构篇 commit阶段

react源码学习-实习篇-状态更新

react源码学习-架构篇-render阶段