react源码分析(9)-commit过程中的Mutation阶段以及DOM节点的清除

目录

删除操作的入口以及相关顺序

删除操作的内部流程

谈谈commit的Mutation阶段中的几个具体方法

总结


2021SC@SDUSC

删除操作的入口以及相关顺序

在commitMutationEffects正式完成对DOM节点的操作,在commitMutationEffects_begin中,将nextEffect初始化为root,然后循环删除其子节点,当子节点为空值时,调用commitMutationEffects_complete,在该方法中首先对Fiber节点进行相对应的更新与插入操作,然后检查是否有兄弟节点,如果存在兄弟节点就将兄弟节点赋值给nexteffect并返回继续由 ommitMutationEffects_begin进行处理(寻找可以删除的节点),假如没有兄弟节点,就将nextEffect赋值为父节点,并继续在commitMutationEffects_complete中处理。这样既可以确保在完成删除操作之后再进行插入或者更新操作。(先执行删除操作是为了减混乱,提高构建以及更新DOM树的正确性)。

function commitMutationEffects_begin(root: FiberRoot) {
  while (nextEffect !== null) {
    const fiber = nextEffect;

    // TODO: Should wrap this in flags check, too, as optimization
    const deletions = fiber.deletions;
    if (deletions !== null) {
      for (let i = 0; i < deletions.length; i++) {
        const childToDelete = deletions[i];
        try {
          commitDeletion(root, childToDelete, fiber);
        } catch (error) {
          reportUncaughtErrorInDEV(error);
          captureCommitPhaseError(childToDelete, fiber, error);
        }
      }
    }

    const child = fiber.child;
    if ((fiber.subtreeFlags & MutationMask) !== NoFlags && child !== null) {
      ensureCorrectReturnPointer(child, fiber);
      nextEffect = child;
    } else {
      commitMutationEffects_complete(root);
    }
  }
}

function commitMutationEffects_complete(root: FiberRoot) {
  while (nextEffect !== null) {
    const fiber = nextEffect;
    setCurrentDebugFiberInDEV(fiber);
    try {
      commitMutationEffectsOnFiber(fiber, root);
    } catch (error) {
      reportUncaughtErrorInDEV(error);
      captureCommitPhaseError(fiber, fiber.return, error);
    }
    resetCurrentDebugFiberInDEV();

    const sibling = fiber.sibling;
    if (sibling !== null) {
      ensureCorrectReturnPointer(sibling, fiber.return);
      nextEffect = sibling;
      return;
    }

    nextEffect = fiber.return;
  }
}

删除操作的内部流程

现在可以正式进行删除操作,首先都需要找到该目标节点中为DOM节点的父节点(Fiber树节点通常比DOM树多),假如目标节点为DOM节点,那么可以在遍历消除其Fiber子节点后(调用commitNestedUnmounts),利用父节点将该子节点删除。假如目标节点并非DOM节点,那么就需要遍历其子节点(此时调用了commitUnmount为了消除自身Fiber节点)直到找到DOM节点为止,找到后消除其子节点并将该DOM节点删除。

被删除DOM节点的类型可能有HostComponent,HostText,DehydratedFragment,HostPortal。大部分使用 commitUnmount 删除其子节点。在其中可以看到许多组件的卸载过程,比如函数组件中useEffect的清除函数(使用return关键字),比如类组件中的生命周期函数componentWillUnmount,以及 HostComponent(衍生标签节点)删除自身的ref等。

谈谈commit的Mutation阶段中的几个具体方法

首先是commitResetTextContent,该方法作用为清空节点的文本内容。该方法会调用 resetTextContent方法,而resetTextContent则会调用setTextContent方法,在该方法中会检验text文本是否存在,如果存在并且节点的子节点只有一个并且为文本节点,那么就将文本节点的nodeValue设置为text,如果text节点不存在,直接设置节点的textContent为text,而我们传入的text节点为‘’,这里测试了一下

所以该方法中会将textContent设置为指定文本。

再来看一下commitDetachRef方法,这里给出源码

function commitDetachRef(current: Fiber) {
  const currentRef = current.ref;
  if (currentRef !== null) {
    if (typeof currentRef === 'function') {
      if (
        enableProfilerTimer &&
        enableProfilerCommitHooks &&
        current.mode & ProfileMode
      ) {
        try {
          startLayoutEffectTimer();
          currentRef(null);
        } finally {
          recordLayoutEffectDuration(current);
        }
      } else {
        currentRef(null);
      }
    } else {
      currentRef.current = null;
    }
  }
}

假如节点有ref需要清空,那就执行该方法。如果ref是函数类型就会查看是否能开启定时器,能就开启进行计时,关于react中各种过期与更新时间的具体作用,我将会在以后展开描述。假如并非函数类型。就会直接将ref指向进行清空。

总结

这里介绍了在commit操作树上节点时删除,插入,更新的执行顺序与过程,重点介绍了删除过程中的内部流程。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值