commitDeletion

unmountHostComponents是一个大的遍历

注意这里只会对非HostComponent的节点查找子树:

} else if (node.tag === HostPortal) {
  currentParent = node.stateNode.containerInfo;
  currentParentIsContainer = true;
  // Visit children because portals might contain host components.
  if (node.child !== null) {
    node.child.return = node;
    node = node.child;
    continue;
  }
} else {
  commitUnmount(node);
  // Visit children because we may find more host components below.
  if (node.child !== null) {
    node.child.return = node;
    node = node.child;
    continue;
  }
}

对于HostComponent只会找兄弟节点,而他的遍历放在了commitNestedUnmounts。注意这是一个递归的过程,在commitNestedUnmounts会对每个子节点调用commitUnmount,内部会对HostPortal类型递归调用unmountHostComponents。同样的在这里也会调用commitUnmount,所以也会递归。

这里真正需要从 DOM 中删除的只有HostComponent,所以这个方法的主逻辑是找到HostComponent并根据父节点的类型执行不同的删除操作。

function commitDeletion(current: Fiber): void {
  if (supportsMutation) {
    unmountHostComponents(current)
  } else {
    commitNestedUnmounts(current)
  }
  detachFiber(current)
}

function unmountHostComponents(current): void {
  let node: Fiber = current

  let currentParentIsValid = false

  // Note: these two variables *must* always be updated together.
  let currentParent
  let currentParentIsContainer

  while (true) {
    if (!currentParentIsValid) {
      let parent = node.return
      findParent: while (true) {
        invariant(
          parent !== null,
          'Expected to find a host parent. This error is likely caused by ' +
            'a bug in React. Please file an issue.',
        )
        switch (parent.tag) {
          case HostComponent:
            currentParent = parent.stateNode
            currentParentIsContainer = false
            break findParent
          case HostRoot:
            currentParent = parent.stateNode.containerInfo
            currentParentIsContainer = true
            break findParent
          case HostPortal:
            currentParent = parent.stateNode.containerInfo
            currentParentIsContainer = true
            break findParent
        }
        parent = parent.return
      }
      currentParentIsValid = true
    }

    if (node.tag === HostComponent || node.tag === HostText) {
      commitNestedUnmounts(node)
      if (currentParentIsContainer) {
        removeChildFromContainer((currentParent: any), node.stateNode)
      } else {
        removeChild((currentParent: any), node.stateNode)
      }
      // Don't visit children because we already visited them.
    } else if (node.tag === HostPortal) {
      currentParent = node.stateNode.containerInfo
      currentParentIsContainer = true
      // Visit children because portals might contain host components.
      if (node.child !== null) {
        node.child.return = node
        node = node.child
        continue
      }
    } else {
      commitUnmount(node)
      // Visit children because we may find more host components below.
      if (node.child !== null) {
        node.child.return = node
        node = node.child
        continue
      }
    }
    if (node === current) {
      return
    }
    while (node.sibling === null) {
      if (node.return === null || node.return === current) {
        return
      }
      node = node.return
      if (node.tag === HostPortal) {
        currentParentIsValid = false
      }
    }
    node.sibling.return = node.return
    node = node.sibling
  }
}

commitNestedUnmounts

这个方法是在发现需要删除的节点是HostComponent的时候调用的

遍历子树,对每个节点调用commitUnmount,遍历过程跟workLoop差不多,遵循深度优先遍历规则

在这个遍历过程中,因为对于每个节点都会调用commitUnmount,所以如果发现有portal节点,则会递归调用unmountHostComponents

function commitNestedUnmounts(root: Fiber): void {
  let node: Fiber = root
  while (true) {
    commitUnmount(node)
    if (node.child !== null && (!supportsMutation || node.tag !== HostPortal)) {
      node.child.return = node
      node = node.child
      continue
    }
    if (node === root) {
      return
    }
    while (node.sibling === null) {
      if (node.return === null || node.return === root) {
        return
      }
      node = node.return
    }
    node.sibling.return = node.return
    node = node.sibling
  }
}

commitUnmount

这个方法是真正的对每个节点执行删除前的操作的,对于

HostComponent

如果有ref则要卸载

ClassComponent

如果有ref则要卸载

如果有componentWillUnmount生命周期方法,则调用之

HostPortal

继续调用unmountHostComponents

function commitUnmount(current: Fiber): void {
  onCommitUnmount(current)

  switch (current.tag) {
    case ClassComponent: {
      safelyDetachRef(current)
      const instance = current.stateNode
      if (typeof instance.componentWillUnmount === 'function') {
        safelyCallComponentWillUnmount(current, instance)
      }
      return
    }
    case HostComponent: {
      safelyDetachRef(current)
      return
    }
    case HostPortal: {
      if (supportsMutation) {
        unmountHostComponents(current)
      } else if (supportsPersistence) {
        emptyPortalContainer(current)
      }
      return
    }
  }
}

results matching ""

    No results matching ""

    Jokcy的二维码

    扫码添加Jokcy,更多更新更优质的前端学习内容不断更新中,期待与你一起成长!

    Jokcy的二维码