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源码Part7——Commit(Mutation阶段): https://segmentfault.com/a/11...
参考链接:React技术揭秘——https://react.iamkasong.com/p...

React中双缓存机制触发——Current指针替换

  • 发生时间点是:在commit阶段中的Mutation阶段之后,Layout阶段之前
  • 原因一:在Mutation阶段时,会执行componentWillUnMount生面周期,此时可能会操作原来Fibre节点的内容;为确保数据的可靠性所以不会修改Current指针
  • 原因三: Mutation阶段完成后,此时已经完成WorkInProgress中Fibre树的渲染
  • 原因二:在Layout阶段会执行ComponentDidMount、ComponentDidUpdate生命周期;为确保数据的纯净和更新,所以在Layout阶段之前发替换Current指针。

Commit中的Layout阶段的执行过程?

  • 入口函数:commitLayoutEffects
  • 准备工作:commitLayoutEffects_begin
  • 预处理:commitLayoutMountEffects_complete
    1. 对于函数组件Hook,useLayoutEffect的回调函数在这里执行
  • 对Fibre节点进行处理:commitLayoutEffectOnFiber

    1. 对于Class组件,componentDidMount和componentDidUpdate生命周期触发

Commit中的Layout阶段做了什么?

  • 对于Class组件而言,在这个阶段会执行ComponentDidMount、ComponentDidUpdate生命周期
  • 对于函数组建Hook而言,会执行useLayoutEffect的回调函数(不包括返回值)

    1. 对于下面这个useEffect,会在Layout阶段执行handleStatusChange函数(create);在Mutation阶段执行useEffect返回的函数(destroy),用于清理副作用

       useEffect(() => {
        function handleStatusChange(status) {
       setIsOnline(status.isOnline);
        }
       
        return () => {
       ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
        };
      });

commitLayoutEffects——入口函数

export function commitLayoutEffects(
  finishedWork: Fiber,
  root: FiberRoot,
  committedLanes: Lanes,
): void {
  inProgressLanes = committedLanes;
  inProgressRoot = root;
  nextEffect = finishedWork;

  commitLayoutEffects_begin(finishedWork, root, committedLanes);

  inProgressLanes = null;
  inProgressRoot = null;
}

commitLayoutEffects_begin——准备工作

image.png

commitLayoutMountEffects_complete——预处理

  • 执行useLayoutEffect的回调函数safelyCallCommitHookLayoutEffectListMount

    1. 可以在switch语句汇总看到,case条件是 SimpleMemoComponent

image.png

  • commitLayoutMountEffects_complete代码片段

    function commitLayoutMountEffects_complete(
    subtreeRoot: Fiber,
    root: FiberRoot,
    committedLanes: Lanes,
    ) {
    // Suspense layout effects semantics don't change for legacy roots.
    const isModernRoot = (subtreeRoot.mode & ConcurrentMode) !== NoMode;
    
    while (nextEffect !== null) {
      const fiber = nextEffect;
    
      if (
        enableSuspenseLayoutEffectSemantics &&
        isModernRoot &&
        offscreenSubtreeWasHidden &&
        !offscreenSubtreeIsHidden
      ) {
        // Inside of an Offscreen subtree that changed visibility during this commit.
        // If this subtree was hidden, layout effects will have already been destroyed (during mutation phase)
        // but if it was just shown, we need to (re)create the effects now.
        // TODO (Offscreen) Check: flags & LayoutStatic
        switch (fiber.tag) {
          case FunctionComponent:
          case ForwardRef:
          case SimpleMemoComponent: {
            if (
              enableProfilerTimer &&
              enableProfilerCommitHooks &&
              fiber.mode & ProfileMode
            ) {
              try {
                startLayoutEffectTimer();
                safelyCallCommitHookLayoutEffectListMount(fiber, fiber.return);
              } finally {
                recordLayoutEffectDuration(fiber);
              }
            } else {
              safelyCallCommitHookLayoutEffectListMount(fiber, fiber.return);
            }
            break;
          }
          case ClassComponent: {
            const instance = fiber.stateNode;
            if (typeof instance.componentDidMount === 'function') {
              safelyCallComponentDidMount(fiber, fiber.return, instance);
            }
            break;
          }
        }
    
        // TODO (Offscreen) Check flags & RefStatic
        switch (fiber.tag) {
          case ClassComponent:
          case HostComponent:
            safelyAttachRef(fiber, fiber.return);
            break;
        }
      } else if ((fiber.flags & LayoutMask) !== NoFlags) {
        const current = fiber.alternate;
        if (__DEV__) {
         // DEV环境可以暂时不管
        
        } else {
          try {
            commitLayoutEffectOnFiber(root, current, fiber, committedLanes);
          } catch (error) {
            captureCommitPhaseError(fiber, fiber.return, error);
          }
        }
      }
    
      if (fiber === subtreeRoot) {
        nextEffect = null;
        return;
      }
    
      const sibling = fiber.sibling;
      if (sibling !== null) {
        ensureCorrectReturnPointer(sibling, fiber.return);
        nextEffect = sibling;
        return;
      }
    
      nextEffect = fiber.return;
    }
    }

commitLayoutEffectOnFiber——处理Fibre节点

  • Class组件的生命周期componentDidMount和componentDidUpdate生命周期在此触发,触发条件就是当前的current指针是null
  • componentDidMount生命周期执行片段
    image.png
  • componentDidUpdate生命周期执行片段
  • image.png

冷咖啡
22 声望3 粉丝

知识的搬运工😄