自学内容网 自学内容网

React16源码: React中commitAllHostEffects内部的commitPlacement源码实现

commitPlacement


1 )概述

  • 在 react commit 阶段的 commitRoot 第二个while循环中
  • 调用了 commitAllHostEffects,在这个函数内部处理了
  • 把一个新的dom节点挂载到真正的dom树上面去的一个过程
  • 现在主要关注下其中调用的 commitPlacement

2 )源码

定位到 packages/react-reconciler/src/ReactFiberCommitWork.js#L850

function commitPlacement(finishedWork: Fiber): void {
  if (!supportsMutation) {
    return;
  }

  // Recursively insert all host nodes into the parent.
  const parentFiber = getHostParentFiber(finishedWork);

  // Note: these two variables *must* always be updated together.
  let parent;
  let isContainer;

  switch (parentFiber.tag) {
    case HostComponent:
      parent = parentFiber.stateNode;
      isContainer = false;
      break;
    case HostRoot:
      parent = parentFiber.stateNode.containerInfo;
      isContainer = true;
      break;
    case HostPortal:
      parent = parentFiber.stateNode.containerInfo;
      isContainer = true;
      break;
    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) {
    // Reset the text content of the parent before doing any insertions
    resetTextContent(parent);
    // Clear ContentReset from the effect tag
    parentFiber.effectTag &= ~ContentReset;
  }

  const before = getHostSibling(finishedWork);
  // We only have the top Fiber that was inserted but we need recurse down its
  // children to find all the terminal nodes.
  let node: Fiber = finishedWork;
  while (true) {
    if (node.tag === HostComponent || node.tag === HostText) {
      if (before) {
        if (isContainer) {
          insertInContainerBefore(parent, node.stateNode, before);
        } else {
          insertBefore(parent, node.stateNode, before);
        }
      } else {
        if (isContainer) {
          appendChildToContainer(parent, node.stateNode);
        } else {
          appendChild(parent, node.stateNode);
        }
      }
    } else if (node.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 if (node.child !== null) {
      node.child.return = node;
      node = node.child;
      continue;
    }
    if (node === finishedWork) {
      return;
    }
    while (node.sibling === null) {
      if (node.return === null || node.return === finishedWork) {
        return;
      }
      node = node.return;
    }
    node.sibling.return = node.return;
    node = node.sibling;
  }
}
  • 进入 getHostParentFiber
    function getHostParentFiber(fiber: Fiber): Fiber {
      let parent = fiber.return;
      while (parent !== null) {
        if (isHostParent(parent)) {
          return parent;
        }
        parent = parent.return;
      }
      invariant(
        false,
        'Expected to find a host parent. This error is likely caused by a bug ' +
          'in React. Please file an issue.',
      );
    }
    
    function isHostParent(fiber: Fiber): boolean {
      return (
        fiber.tag === HostComponent ||
        fiber.tag === HostRoot ||
        fiber.tag === HostPortal
      );
    }
    
    • 显然意见,这个循环就是向上查找到第一个 HostComponentHostRootHostPortal
  • 之后,判断 parentFiber.tag,对不同条件的 isContainer 进行赋值
  • 之后,判断 ContentReset 是否存在,存在则对文本节点进行替换
  • 进入 getHostSibling 向上找到 sibling 节点,下面这个英文注释留着 这个是查找sibling的核心算法
    function getHostSibling(fiber: Fiber): ?Instance {
      // We're going to search forward into the tree until we find a sibling host
      // node. Unfortunately, if multiple insertions are done in a row we have to
      // search past them. This leads to exponential search for the next sibling.
      // TODO: Find a more efficient way to do this.
      let node: Fiber = fiber;
      siblings: while (true) {
        // If we didn't find anything, let's try the next sibling.
        while (node.sibling === null) {
          if (node.return === null || isHostParent(node.return)) {
            // If we pop out of the root or hit the parent the fiber we are the
            // last sibling.
            return null;
          }
          node = node.return;
        }
        node.sibling.return = node.return;
        node = node.sibling;
        // 这个循环要找兄弟节点中的第一个dom节点
        // 如果兄弟节点不是 HostComponent 或 HostText 往下去找
        // 子节点中的第一个dom节点
        while (node.tag !== HostComponent && node.tag !== HostText) {
          // If it is not host node and, we might have a host node inside it.
          // Try to search down until we find one.
          if (node.effectTag & Placement) {
            // If we don't have a child, try the siblings instead.
            continue siblings;
          }
          // If we don't have a child, try the siblings instead.
          // We also skip portals because they are not part of this host tree.
          if (node.child === null || node.tag === HostPortal) {
            continue siblings;
          } else {
            node.child.return = node;
            node = node.child;
          }
        }
        // Check if this host node is stable or about to be placed.
        if (!(node.effectTag & Placement)) {
          // Found it!
          return node.stateNode;
        }
      }
    }
    
  • 接下去又进入一个while循环
    • 里面的第一个判断,node.tag === HostComponent || node.tag === HostText
    • 只有 HostComponent 和 HostText 才有插入dom的需要
    • 注意,这里一系列的 if-else 是操作dom的核心

原文地址:https://blog.csdn.net/Tyro_java/article/details/135863654

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!