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);
          }
        }
      }
    }
    

      



  • 相关阅读:
    codeforces 814B An express train to reveries
    codeforces 814A An abandoned sentiment from past
    codeforces 785D D. Anton and School
    codeforces 785C Anton and Fairy Tale
    codeforces 791C Bear and Different Names
    AOP详解
    Spring集成JUnit测试
    Spring整合web开发
    IOC装配Bean(注解方式)
    IOC装配Bean(XML方式)
  • 原文地址:https://www.cnblogs.com/gqx-html/p/14832744.html
Copyright © 2011-2022 走看看