Skip to content
目录

commitDeletion 删除节点

commitDeletion具体做了以下几件事:

  • 针对DOM节点,深度优先卸载ref,其次删除DOM节点
  • 针对class组件,先卸载ref, 其次,深度优先遍历子节点,然后卸载ref以及删除DOM,并调用 componentWillUnmount 声明周期钩子函数;
  • 针对function组件,跟class组件一致,会按照useEffect和useLayoutEffect的书写顺序,依次调用销毁函数;
typescript
function commitDeletion(current: Fiber): void {
  if (supportsMutation) {
    // ReactDOM走这个
    unmountHostComponents(current);
  } else {
    // Detach refs and call componentWillUnmount() on the whole subtree.
    commitNestedUnmounts(current);
  }
  // 卸载Ref
  detachFiber(current);
}

unmountHostComponents

这个方法主要做的就是具体删除操作;这个是一个深度优先的过程。

  1. 先找到当前节点的父节点(真实dom)
  2. 然后进行一个深度优先的遍历,优先子节点,在子节点的父节点,在父节点
typescript
function unmountHostComponents(current): void {
  let node: Fiber = current;
  // 标记当前的parent是否是一个真实的DOM节点
  let currentParentIsValid = false;
  let currentParent;
  let currentParentIsContainer;
  while (true) {
    if (!currentParentIsValid) {
      let parent = node.return;
      // 这个循环的做的事情,其实就是向Fiber树向上查找
      // 找到最近的为真实DOM父节点节点的Fiber
      findParent: while (true) {
        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);
      // 执行真实DOM节点的removeChild操作
      if (currentParentIsContainer) {
        removeChildFromContainer(
          ((currentParent: any): Container),
          (node.stateNode: Instance | TextInstance),
        );
      } else {
        removeChild(
          ((currentParent: any): Instance),
          (node.stateNode: Instance | TextInstance),
        );
      }
    } else {
      // 提交卸载 
      commitUnmount(node);
      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;
  }
}

commitUnmount

  • 针对函数组件, 调用useEffect和useLayoutEffect的卸载函数
  • 针对class组件, 调用componentWillUnmount
  • 针对DOM组件, 执行卸载ref相关操作
typescript
function commitUnmount(current: Fiber): void {
  switch (current.tag) {
    case FunctionComponent:
    case ForwardRef:
    case MemoComponent:
    case SimpleMemoComponent: {
      const updateQueue: FunctionComponentUpdateQueue | null = (current.updateQueue: any);
      if (updateQueue !== null) {
        const lastEffect = updateQueue.lastEffect;
        if (lastEffect !== null) {
          const firstEffect = lastEffect.next;
          let effect = firstEffect;
          do {
            const destroy = effect.destroy;
            if (destroy !== undefined) {
              safelyCallDestroy(current, destroy);
            }
            effect = effect.next;
          } while (effect !== firstEffect);
        }
      }
      break;
    }
    case ClassComponent: {
      safelyDetachRef(current);
      const instance = current.stateNode;
      if (typeof instance.componentWillUnmount === 'function') {
        safelyCallComponentWillUnmount(current, instance);
      }
      return;
    }
    case HostComponent: {
      safelyDetachRef(current);
      return;
    }
  }
}

commitNestedUnmounts

当前方法很commitUnmount方法其实做的事情差不多,只不过这里是针对纯DOM树节点,进行一个遍历卸载ref的过程

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

detachFiber

将当前删除的节点的fiber对象置空,并且从Fiber链表上移除

typescript
function detachFiber(current: Fiber) {
  current.return = null;
  current.child = null;
  current.memoizedState = null;
  current.updateQueue = null;
  const alternate = current.alternate;
  if (alternate !== null) {
    alternate.return = null;
    alternate.child = null;
    alternate.memoizedState = null;
    alternate.updateQueue = null;
  }
}

备案号: 浙ICP备2023000081号