Skip to content

commitBeforeMutationEffects

commitRootImpl() 中会调用 commitBeforeMutationEffects,进入 before mutation 阶段(即执行 DOM 操作前)

这个阶段的执行过程和 render 阶段 类似,一样存在 “递”“归” 的逻辑,通过深度优先遍历,找到最后一个有 BeforeMutationMask 标记的 Fiber,然后从下往上调用 complete 逻辑

commitBeforeMutationEffects 流程图

commitBeforeMutationEffects

ts

    const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(root, finishedWork)

源码地址 commitBeforeMutationEffects() | react-reconciler/src/ReactFiberCommitWork.old.js

ts

    export function commitBeforeMutationEffects(
      root: FiberRoot, // 即 fiberRootNode
      firstChild: Fiber // 即 rootFiber
    ) {
      // 根据 enableCreateEventHandleAPI 的值来决定是否启用新的事件处理方式(默认为 false,该函数返回值为 null)
      focusedInstanceHandle = prepareForCommit(root.containerInfo)
    
      nextEffect = firstChild
    
      // 处理 before mutation 阶段的副作用
      commitBeforeMutationEffects_begin()
    
      // We no longer need to track the active instance fiber
      const shouldFire = shouldFireAfterActiveInstanceBlur
      shouldFireAfterActiveInstanceBlur = false
      focusedInstanceHandle = null
    
      return shouldFire
    }

commitBeforeMutationEffects_begin

源码地址 commitBeforeMutationEffects_begin() | react-reconciler/src/ReactFiberCommitWork.old.js

ts

    function commitBeforeMutationEffects_begin() {
      // 对 Fiber 树进行深度优先遍历,当找到符合条件的节点时开始执行对应的操作
      while (nextEffect !== null) {
        const fiber = nextEffect
    
        // This phase is only used for beforeActiveInstanceBlur.
        // Let's skip the whole loop if it's off.
        // enableCreateEventHandleAPI 值为 false 可以忽略
        if (enableCreateEventHandleAPI) {
          // 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 deletion = deletions[i]
              commitBeforeMutationEffectsDeletion(deletion)
            }
          }
        }
    
        const child = fiber.child
    
        // 找最后一个带有 BeforeMutationMask 标识的 Fiber
        if (
          // 判断当前 Fiber 的子 Fiber 树中是否存在 BeforeMutationMask 副作用(即 Update | Snapshot)
          (fiber.subtreeFlags & BeforeMutationMask) !== NoFlags &&
          // 判断当前 Fiber 是否存在子 Fiber
          child !== null
        ) {
          // 更新指针
          child.return = fiber
          nextEffect = child
        } else {
          // 找最后一个带有 BeforeMutationMask 标识的 Fiber 时,进入该 Fiber 的 complete 阶段
          commitBeforeMutationEffects_complete()
        }
      }
    }

commitBeforeMutationEffects_complete

源码地址 commitBeforeMutationEffects_complete() | react-reconciler/src/ReactFiberCommitWork.old.js

ts

    function commitBeforeMutationEffects_complete() {
      while (nextEffect !== null) {
        const fiber = nextEffect
    
        try {
          // before mutation 阶段的主要函数
          commitBeforeMutationEffectsOnFiber(fiber)
        } catch (error) {
          captureCommitPhaseError(fiber, fiber.return, error)
        }
    
        const sibling = fiber.sibling
        // 判断是否存在兄弟节点
        if (sibling !== null) {
          // 当存在兄弟节点时更新指针并返回到 begin 方法继续向下遍历
          sibling.return = fiber.return
          nextEffect = sibling
          return
        }
    
        // 更新指针为父节点
        nextEffect = fiber.return
      }
    }

commitBeforeMutationEffectsOnFiber

before mutation 阶段的主要函数,其内部会根据 flags 的值来判断是否存在 Snapshot 副作用,如果存在则会根据 tag 的值来执行对应的操作:

  1. 执行类组件 getSnapshotBeforeUpdate 生命周期函数
  2. rootFiber 的容器进行清空操作

源码地址 commitBeforeMutationEffectsOnFiber | react-reconciler/src/ReactFiberCommitWork.old.js

ts

    function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
      const current = finishedWork.alternate
      const flags = finishedWork.flags
    
      // 未开启 enableCreateEventHandleAPI 可忽略
      if (enableCreateEventHandleAPI) {
        if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
          // Check to see if the focused element was inside of a hidden (Suspense) subtree.
          // TODO: Move this out of the hot path using a dedicated effect tag.
          if (
            finishedWork.tag === SuspenseComponent &&
            isSuspenseBoundaryBeingHidden(current, finishedWork) &&
            doesFiberContain(finishedWork, focusedInstanceHandle)
          ) {
            shouldFireAfterActiveInstanceBlur = true
            beforeActiveInstanceBlur(finishedWork)
          }
        }
      }
    
      // 判断是否存在 Snapshot 副作用
      if ((flags & Snapshot) !== NoFlags) {
        switch (finishedWork.tag) {
          case FunctionComponent:
          case ForwardRef:
          case SimpleMemoComponent: {
            break
          }
          // 类组件
          case ClassComponent: {
            if (current !== null) {
              const prevProps = current.memoizedProps
              const prevState = current.memoizedState
              const instance = finishedWork.stateNode
              // We could update instance props and state here,
              // but instead we rely on them being set during last render.
              // TODO: revisit this when we implement resuming.
    
              // 执行类组件的 getSnapshotBeforeUpdate 生命周期函数
              const snapshot = instance.getSnapshotBeforeUpdate(
                finishedWork.elementType === finishedWork.type
                  ? prevProps
                  : resolveDefaultProps(finishedWork.type, prevProps),
                prevState
              )
              // 将 getSnapshotBeforeUpdate 的返回值赋值给 __reactInternalSnapshotBeforeUpdate 属性
              instance.__reactInternalSnapshotBeforeUpdate = snapshot
            }
            break
          }
          // rootFiber
          case HostRoot: {
            if (supportsMutation) {
              const root = finishedWork.stateNode
              // 清空容器
              clearContainer(root.containerInfo)
            }
            break
          }
          case HostComponent:
          case HostText:
          case HostPortal:
          case IncompleteClassComponent:
            // Nothing to do for these component types
            break
          default: {
            throw new Error(
              'This unit of work tag should not have side-effects. This error is ' +
                'likely caused by a bug in React. Please file an issue.'
            )
          }
        }
      }
    }

转载自 maomao1996