乐趣区

关于前端:React-render阶段解析三completeWork流程

这次是接着上一期 render 阶段的文章,解析挂载时 render 阶段的 ” 归 ” 阶段 —-completeWork 函数

completeWork 开始阶段

在 performUnitOfWork 中执行完 beginWork,就会进入上面判断

  if (next === null) {
    // workInProgress 曾经不存在子树,就开始进行 "归" 阶段
    completeUnitOfWork(unitOfWork);
  } else {
    // next 是 beginWork 调用后的返回值 workInProgress.child
    workInProgress = next;
  }

completeUnitOfWork

次要做了两件事,执行 completeWork 和收拢 EffectList

function completeUnitOfWork(unitOfWork: Fiber): void {
  // 尝试实现以后的工作单元,而后挪动到下一个兄弟。如果没有更多的兄弟,返回到父 fiber。let completedWork = unitOfWork;
  // 直到父节点为 null,示意整棵 workInProgress fiber 树已处理完毕。do {
    // 记录父节点和以后节点的 current 树
    const current = completedWork.alternate;
    const returnFiber = completedWork.return;

    // 查看工作是否实现或是否有货色抛出.
    if ((completedWork.effectTag & Incomplete) === NoEffect) {
      let next;
      if (
        !enableProfilerTimer ||
        (completedWork.mode & ProfileMode) === NoMode
      ) {
        // 执行 completeWork,并把返回值赋值给 next
        next = completeWork(current, completedWork, subtreeRenderLanes);
      } else {startProfilerTimer(completedWork);
        next = completeWork(current, completedWork, subtreeRenderLanes);
        // Update render duration assuming we didn't error.
        stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);
      }
      resetCurrentDebugFiberInDEV();

      if (next !== null) {
        // Completing this fiber spawned new work. Work on that next.
        workInProgress = next;
        return;
      }

      resetChildLanes(completedWork);

      if (
        returnFiber !== null &&
        (returnFiber.effectTag & Incomplete) === NoEffect
      ) {
        // 执行 effect 相干
        if (returnFiber.firstEffect === null) {returnFiber.firstEffect = completedWork.firstEffect;}
        if (completedWork.lastEffect !== null) {if (returnFiber.lastEffect !== null) {returnFiber.lastEffect.nextEffect = completedWork.firstEffect;}
          returnFiber.lastEffect = completedWork.lastEffect;
        }

        if (effectTag > PerformedWork) {if (returnFiber.lastEffect !== null) {returnFiber.lastEffect.nextEffect = completedWork;} else {returnFiber.firstEffect = completedWork;}
          returnFiber.lastEffect = completedWork;
        }
      }
    } else {const next = unwindWork(completedWork, subtreeRenderLanes);

      // Because this fiber did not complete, don't reset its expiration time.

      if (next !== null) {
        next.effectTag &= HostEffectMask;
        workInProgress = next;
        return;
      }

      if (
        enableProfilerTimer &&
        (completedWork.mode & ProfileMode) !== NoMode
      ) {stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);

        let actualDuration = completedWork.actualDuration;
        let child = completedWork.child;
        while (child !== null) {
          actualDuration += child.actualDuration;
          child = child.sibling;
        }
        completedWork.actualDuration = actualDuration;
      }

      if (returnFiber !== null) {
        // Mark the parent fiber as incomplete and clear its effect list.
        returnFiber.firstEffect = returnFiber.lastEffect = null;
        returnFiber.effectTag |= Incomplete;
      }
    }

    const siblingFiber = completedWork.sibling;
    if (siblingFiber !== null) {
      // If there is more work to do in this returnFiber, do that next.
      workInProgress = siblingFiber;
      return;
    }
    // 赋值父节点
    completedWork = returnFiber;
    workInProgress = completedWork;
  } while (completedWork !== null);

  // We've reached the root.
  if (workInProgressRootExitStatus === RootIncomplete) {workInProgressRootExitStatus = RootCompleted;}
}

运行流程

completeWork

如果说“递”阶段的 beginWork 办法次要是创立子节点,那么“归”阶段的 completeWork 办法则次要是创立以后节点的 DOM 节点,并对子节点的 DOM 节点和 EffectList 进行收拢。很多类型是不进行解决,return null

function completeWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
): Fiber | null {
  const newProps = workInProgress.pendingProps;

  switch (workInProgress.tag) {
    case IndeterminateComponent:
    case LazyComponent:
    case SimpleMemoComponent:
    case FunctionComponent:
    case ForwardRef:
    case Fragment:
    case Mode:
    case Profiler:
    case ContextConsumer:
    case MemoComponent:
      return null;
    case ClassComponent: {
      // ... 省略
      return null;
    }
    case HostRoot: {
      // ... 省略
      return null;
    }
    case HostComponent: {
      // ...
      const type = workInProgress.type;
      if (current !== null && workInProgress.stateNode != null) {
      // 更新 dom 节点
        updateHostComponent(
          current,
          workInProgress,
          type,
          newProps,
          rootContainerInstance,
        );

        if (enableDeprecatedFlareAPI) {
          const prevListeners = current.memoizedProps.DEPRECATED_flareListeners;
          const nextListeners = newProps.DEPRECATED_flareListeners;
          if (prevListeners !== nextListeners) {markUpdate(workInProgress);
          }
        }

        if (current.ref !== workInProgress.ref) {markRef(workInProgress);
        }
      } else {
        //...
        const currentHostContext = getHostContext();
        const wasHydrated = popHydrationState(workInProgress);
        if (wasHydrated) {// 服务端渲染相干} else {
          // 创立新的 dom 节点
          const instance = createInstance(
            type,
            newProps,
            rootContainerInstance,
            currentHostContext,
            workInProgress,
          );
          // 把 fiber 子节点的 dom 挂载到以后 dom 前面
          appendAllChildren(instance, workInProgress, false, false);

          workInProgress.stateNode = instance;

          if (enableDeprecatedFlareAPI) {
            const listeners = newProps.DEPRECATED_flareListeners;
            if (listeners != null) {
              updateDeprecatedEventListeners(
                listeners,
                workInProgress,
                rootContainerInstance,
              );
            }
          }

          if (
            // 初始化 dom 属性和事件
            finalizeInitialChildren(
              instance,
              type,
              newProps,
              rootContainerInstance,
              currentHostContext,
            )
          ) {markUpdate(workInProgress);
          }
        }

        if (workInProgress.ref !== null) {markRef(workInProgress);
        }
      }
      return null;
    }
  // ... 省略
}

createInstance

新建节点,调用 createElement 创立 dom 节点

function createInstance(
  type: string,
  props: Props,
  rootContainerInstance: Container,
  hostContext: HostContext,
  internalInstanceHandle: Object,
): Instance {
  let parentNamespace: string;
  if (__DEV__) {
    // TODO: take namespace into account when validating.
    const hostContextDev = ((hostContext: any): HostContextDev);
    validateDOMNesting(type, null, hostContextDev.ancestorInfo);
    if (
      typeof props.children === 'string' ||
      typeof props.children === 'number'
    ) {
      const string = '' + props.children;
      const ownAncestorInfo = updatedAncestorInfo(
        hostContextDev.ancestorInfo,
        type,
      );
      validateDOMNesting(null, string, ownAncestorInfo);
    }
    parentNamespace = hostContextDev.namespace;
  } else {parentNamespace = ((hostContext: any): HostContextProd);
  }
  const domElement: Instance = createElement(
    type,
    props,
    rootContainerInstance,
    parentNamespace,
  );
  precacheFiberNode(internalInstanceHandle, domElement);
  // 更新属性
  updateFiberProps(domElement, props);
  return domElement;
}

appendAllChildren

把 fiber 子节点的 dom 挂载到以后 dom 前面

 appendAllChildren = function(
    parent: Instance,
    workInProgress: Fiber,
    needsVisibilityToggle: boolean,
    isHidden: boolean,
  ) {
    let node = workInProgress.child;
    while (node !== null) {if (node.tag === HostComponent || node.tag === HostText) {
        // stateNode 挂载节点的 dom
        appendInitialChild(parent, node.stateNode);
      } else if (enableFundamentalAPI && node.tag === FundamentalComponent) {appendInitialChild(parent, node.stateNode.instance);
      } else if (node.tag === HostPortal) {} else if (node.child !== null) {
        // 针对一些非凡类型的子节点,如 <Fragment />,尝试从子节点的子节点获取 DOM
        // 存在子节点就持续遍历子节点
        node.child.return = node;
        node = node.child;
        continue;
      }
      if (node === workInProgress) {return;}
      while (node.sibling === null) {if (node.return === null || node.return === workInProgress) {return;}
        node = node.return;
      }
      node.sibling.return = node.return;
      // 将 node.sibling 作为下次循环的主体
      node = node.sibling;
    }
  };

// 执行了原生的 appendChild 办法
export function appendInitialChild(parentInstance: Instance, child: Instance | TextInstance): void {parentInstance.appendChild(child);
}

updateHostComponent

更新旧的 dom 节点,次要作用就是计算出须要变动的 DOM 节点属性,并给以后节点打上 Update 的 EffectTag。

updateHostComponent = function(
    current: Fiber,
    workInProgress: Fiber,
    type: Type,
    newProps: Props,
    rootContainerInstance: Container,
  ) {
    // If we have an alternate, that means this is an update and we need to
    // schedule a side-effect to do the updates.
    const oldProps = current.memoizedProps;
    // props 没有变动就间接返回
    if (oldProps === newProps) {return;}

    
    const updatePayload = prepareUpdate(
      instance,
      type,
      oldProps,
      newProps,
      rootContainerInstance,
      currentHostContext,
    );
    // 将计算出来的 updatePayload 挂载在 workInProgress.updateQueue 上,供后续 commit 阶段应用
    workInProgress.updateQueue = (updatePayload: any);
    // 如果 updatePayload 不为空,则给以后节点打上 Update 的 EffectTag
    if (updatePayload) {markUpdate(workInProgress);
    }
  };

总结

  1. completeUnitOfWork 办法次要循环执行 completeWork,父元素为空或者存在兄弟节点就会进行下一轮 render 阶段解析,生成兄弟节点的 fiber。
  2. completeWork 次要是生成以后 fiber 的 dom 节点,并且挂载连接子节点的 dom
  3. completeWork 次要应用 createInstance 新建节点和 updateHostComponent 更新节点操作。
  4. 最终完结 completeUnitOfWork 执行,进入 commit 阶段(下个文章开始讲,敬请期待)

退出移动版