zoukankan      html  css  js  c++  java
  • 请简述 React 16 版本中 commit 阶段的三个子阶段分别做了什么事情

    1. before mutation阶段 (操作 Dom 前)

    更新情况下:`主要调用类组件生命周期函数getSnapshotBeforeUpdate,并且把旧的props和旧的states传递进去

    // 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;
      }
    }
    
        class MyComponent extends React.Component {
          divRef = React.createRef();
          getSnapshotBeforeUpdate(prevProps, prevState) {
            log.push('getSnapshotBeforeUpdate');
            expect(this.divRef.current.textContent).toBe(
              `value:${prevProps.value}`,
            );
            return 'foobar';
          }
        }
    

      

      

    2. mutation阶段 (执行 Dom 操作)

    `获取对象的 effects, 根据不同的 effectTag 执行不同的操作,插入,更新,删除。将 workInProgress Fiber 树变成 current Fiber 树

    ``插入节点:commitPlacement
    ``更新节点:commitWork
    ``删除节点:commitDeletion
     
    // 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;
      }
    }
    // 挂载 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);
      }
    }
    
    // 向容器中追加 | 插入到某一个节点的前面
    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;
          }
        }
      }
    }
    
    // 将 child 插入到父级中
    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);
      }
      // This container might be used for a portal.
      // If something inside a portal is clicked, that click should bubble
      // through the React tree. However, on Mobile Safari the click would
      // never bubble through the *DOM* tree unless an ancestor with onclick
      // event exists. So we wouldn't see it and dispatch it.
      // This is why we ensure that non React root containers have inline onclick
      // defined.
      // https://github.com/facebook/react/issues/11918
      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));
      }
    }
    
    
    root.current = finishedWork
    

      

    3. Layout 阶段 (执行 Dom 操作后)

    `调用类组件的生命周期

    ``初次渲染阶段调用componentDidMount生命周期函数
    ``更新阶段调用componentDidUpdate生命周期函数
    ``执行渲染完成之后的回调函数,也就是render函数的第三个参数,并且更改this指向,指向render方法的第一个参数

    `调用函数组件的钩子函数

    ``firstEffect:指向第一个更新的节点
    ``nextEffect:指向下一个更新的节点
     
    // 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;
      }
    }
    
    // 类组件处理生命周期函数
    // 函数组件处理钩子函数
    function commitLifeCycles(
      finishedRoot: FiberRoot,
      current: Fiber | null,
      finishedWork: Fiber,
      committedExpirationTime: ExpirationTime,
    ): void {
      switch (finishedWork.tag) {
        case ClassComponent: {
          // 获取类组件实例对象
          const instance = finishedWork.stateNode;
          // 如果在类组件中存在生命周期函数判断条件就会成立
          if (finishedWork.effectTag & Update) {
            // 初始渲染阶段
            if (current === null) {
              startPhaseTimer(finishedWork, 'componentDidMount');
              if (__DEV__) {
                if (
                  finishedWork.type === finishedWork.elementType &&
                  !didWarnAboutReassigningProps
                ) {
                  if (instance.props !== finishedWork.memoizedProps) {
                    console.error(
                      'Expected %s props to match memoized props before ' +
                        'componentDidMount. ' +
                        'This might either be because of a bug in React, or because ' +
                        'a component reassigns its own `this.props`. ' +
                        'Please file an issue.',
                      getComponentName(finishedWork.type) || 'instance',
                    );
                  }
                  if (instance.state !== finishedWork.memoizedState) {
                    console.error(
                      'Expected %s state to match memoized state before ' +
                        'componentDidMount. ' +
                        'This might either be because of a bug in React, or because ' +
                        'a component reassigns its own `this.props`. ' +
                        'Please file an issue.',
                      getComponentName(finishedWork.type) || 'instance',
                    );
                  }
                }
              }
              // 调用 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');
              if (__DEV__) {
                if (
                  finishedWork.type === finishedWork.elementType &&
                  !didWarnAboutReassigningProps
                ) {
                  if (instance.props !== finishedWork.memoizedProps) {
                    console.error(
                      'Expected %s props to match memoized props before ' +
                        'componentDidUpdate. ' +
                        'This might either be because of a bug in React, or because ' +
                        'a component reassigns its own `this.props`. ' +
                        'Please file an issue.',
                      getComponentName(finishedWork.type) || 'instance',
                    );
                  }
                  if (instance.state !== finishedWork.memoizedState) {
                    console.error(
                      'Expected %s state to match memoized state before ' +
                        'componentDidUpdate. ' +
                        'This might either be because of a bug in React, or because ' +
                        'a component reassigns its own `this.props`. ' +
                        'Please file an issue.',
                      getComponentName(finishedWork.type) || 'instance',
                    );
                  }
                }
              }
              // 调用 componentDidUpdate 生命周期函数
              // instance.__reactInternalSnapshotBeforeUpdate 快照
              // getSnapShotBeforeUpdate 方法的返回值
              instance.componentDidUpdate(
                prevProps,
                prevState,
                instance.__reactInternalSnapshotBeforeUpdate,
              );
              stopPhaseTimer();
            }
          }
          // 获取任务队列
          const updateQueue = finishedWork.updateQueue;
          // 如果任务队列存在
          if (updateQueue !== null) {
            if (__DEV__) {
              if (
                finishedWork.type === finishedWork.elementType &&
                !didWarnAboutReassigningProps
              ) {
                if (instance.props !== finishedWork.memoizedProps) {
                  console.error(
                    'Expected %s props to match memoized props before ' +
                      'processing the update queue. ' +
                      'This might either be because of a bug in React, or because ' +
                      'a component reassigns its own `this.props`. ' +
                      'Please file an issue.',
                    getComponentName(finishedWork.type) || 'instance',
                  );
                }
                if (instance.state !== finishedWork.memoizedState) {
                  console.error(
                    'Expected %s state to match memoized state before ' +
                      'processing the update queue. ' +
                      'This might either be because of a bug in React, or because ' +
                      'a component reassigns its own `this.props`. ' +
                      'Please file an issue.',
                    getComponentName(finishedWork.type) || 'instance',
                  );
                }
              }
            }
            /**
             * 调用 ReactElement 渲染完成之后的回调函数
             * 即 render 方法的第三个参数
             */
            commitUpdateQueue(finishedWork, updateQueue, instance);
          }
          return;
        }
      }
    }
    
    /**
     * 执行渲染完成之后的回调函数
     */
    export function commitUpdateQueue<State>(
      finishedWork: Fiber,
      finishedQueue: UpdateQueue<State>,
      instance: any,
    ): void {
      // effects 为数组, 存储任务对象 (Update 对象)
      // 但前提是在调用 render 方法时传递了回调函数, 就是 render 方法的第三个参数
      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;
            // 执行回调函数
            callCallback(callback, instance);
          }
        }
      }
    }
    

      



  • 相关阅读:
    实验 7 综合练习一
    实验或作业模版: 实验 6-1 最大公约数 最小公倍数
    实验 6 数组1
    Pro
    作业 4 函数应用
    老大
    双端队列
    zxa and leaf
    Baby Ming and Matrix games
    The more, The Better
  • 原文地址:https://www.cnblogs.com/gqx-html/p/14832744.html
Copyright © 2011-2022 走看看