美文网首页
react-reconsiler模块 - React源码解析(一

react-reconsiler模块 - React源码解析(一

作者: 请叫我Pro大叔 | 来源:发表于2020-03-21 12:11 被阅读0次

简述

react-reconsiler模块是React中最核心的模块之一,也是最复杂的模块之一。其中包括React Fiber框架、React Hooks等的实现。
本文主要讲述其中React初始化的主要过程。

初始化过程

简单的来看,初始化过程可以分为三个阶段:

  1. 第一阶段为创建虚拟DOM的根节点直到调用scheduleWork进行调度更新作业前;
  2. 第二阶段为WorkLoop阶段,该阶段采用深度优先方式遍历整个虚拟DOM,并根据虚拟DOM生成对应的HTML元素挂载到文档中。
  3. 第三阶段为CommitWork阶段,该阶段处理类组件的componentDidMount以及函数组件的Effect Hooks.

创建虚拟DOM的根节点

调用createContainer函数创建一个FiberRoot节点。例如react-dom模块中FiberRoot是在createLegacyRoot函数中创建ReactDOMBlockingRoot实例时创建的,同时创建第一个FiberNode根节点(FiberNodeFiberRoot是两种不同的数据结构,FiberNode与React JSX节点一一对应,即虚拟DOM节点)。
根FiberNode节点的WorkTag类型为HostRoot

ReactDOM.render
 => legacyRenderSubtreeIntoContainer
   => legacyCreateRootFromDOMContainer
      => createLegacyRoot
         => new ReactDOMBlockingRoot(...)
            => createContainer
               => createFiberRoot
                  => new FiberRootNode
export function createFiberRoot(
  containerInfo: any,
  tag: RootTag,
  hydrate: boolean,
  hydrationCallbacks: null | SuspenseHydrationCallbacks,
): FiberRoot {
  // 创建FiberRoot
  const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
  if (enableSuspenseCallback) {
    root.hydrationCallbacks = hydrationCallbacks;
  }

  // Cyclic construction. This cheats the type system right now because
  // stateNode is any.
  // 创建第一个FiberNode
  const uninitializedFiber = createHostRootFiber(tag);
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;

  // 初始化更新队列
  initializeUpdateQueue(uninitializedFiber);

  return root;
}

WorkLoop阶段

React调用updateContainer函数更新容器。例如react-domlegacyRenderSubtreeIntoContainer函数中:

// Initial mount should not be batched.
    unbatchedUpdates(() => {
      updateContainer(children, fiberRoot, parentComponent, callback);
    });

updateContainer函数最终调用workLoopSync函数实现对虚拟DOM的遍历。workLoopSync通过循环调用performUnitOfWork函数来处理WIP(workInProgress)。

updateContainer
  => scheduleWork
    => performSyncWorkOnRoot
      => workLoopSync

第一个WIP从根节点生成。

function performSyncWorkOnRoot(root) {
  // ...
  if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {
    prepareFreshStack(root, expirationTime);
    startWorkOnPendingInteractions(root, expirationTime);
  }
  // ...
}

function prepareFreshStack(root, expirationTime) {
  // ...
  workInProgressRoot = root;
  workInProgress = createWorkInProgress(root.current, null);
  renderExpirationTime = expirationTime;
  workInProgressRootExitStatus = RootIncomplete;
  workInProgressRootFatalError = null;
  workInProgressRootLatestProcessedExpirationTime = Sync;
  workInProgressRootLatestSuspenseTimeout = Sync;
  workInProgressRootCanSuspendUsingConfig = null;
  workInProgressRootNextUnprocessedUpdateTime = NoWork;
  workInProgressRootHasPendingPing = false;
  // ...
}

performUnitOfWork函数处理WIP分为两个过程:

  • beginWork过程,根据WIP的WorkTag完成WIP的初始化工作,同时生成对应子FiberNode(即对应WIP)。
switch (workInProgress.tag) {
    case IndeterminateComponent: {
      return mountIndeterminateComponent(
        current,
        workInProgress,
        workInProgress.type,
        renderExpirationTime,
      );
    }
    case LazyComponent: {
      const elementType = workInProgress.elementType;
      return mountLazyComponent(
        current,
        workInProgress,
        elementType,
        updateExpirationTime,
        renderExpirationTime,
      );
    }
    case FunctionComponent: {
      const Component = workInProgress.type;
      const unresolvedProps = workInProgress.pendingProps;
      const resolvedProps =
        workInProgress.elementType === Component
          ? unresolvedProps
          : resolveDefaultProps(Component, unresolvedProps);
      return updateFunctionComponent(
        current,
        workInProgress,
        Component,
        resolvedProps,
        renderExpirationTime,
      );
    }
    case ClassComponent: {
      const Component = workInProgress.type;
      const unresolvedProps = workInProgress.pendingProps;
      const resolvedProps =
        workInProgress.elementType === Component
          ? unresolvedProps
          : resolveDefaultProps(Component, unresolvedProps);
      return updateClassComponent(
        current,
        workInProgress,
        Component,
        resolvedProps,
        renderExpirationTime,
      );
    }
    case HostRoot:
      return updateHostRoot(current, workInProgress, renderExpirationTime);
    case HostComponent:
      return updateHostComponent(current, workInProgress, renderExpirationTime);
    case HostText:
      return updateHostText(current, workInProgress);
    case SuspenseComponent:
      return updateSuspenseComponent(
        current,
        workInProgress,
        renderExpirationTime,
      );
    case HostPortal:
      return updatePortalComponent(
        current,
        workInProgress,
        renderExpirationTime,
      );
    case ForwardRef: {
      const type = workInProgress.type;
      const unresolvedProps = workInProgress.pendingProps;
      const resolvedProps =
        workInProgress.elementType === type
          ? unresolvedProps
          : resolveDefaultProps(type, unresolvedProps);
      return updateForwardRef(
        current,
        workInProgress,
        type,
        resolvedProps,
        renderExpirationTime,
      );
    }
    case Fragment:
      return updateFragment(current, workInProgress, renderExpirationTime);
    case Mode:
      return updateMode(current, workInProgress, renderExpirationTime);
    case Profiler:
      return updateProfiler(current, workInProgress, renderExpirationTime);
    case ContextProvider:
      return updateContextProvider(
        current,
        workInProgress,
        renderExpirationTime,
      );
    case ContextConsumer:
      return updateContextConsumer(
        current,
        workInProgress,
        renderExpirationTime,
      );
    case MemoComponent: {
      const type = workInProgress.type;
      const unresolvedProps = workInProgress.pendingProps;
      // Resolve outer props first, then resolve inner props.
      let resolvedProps = resolveDefaultProps(type, unresolvedProps);
      if (__DEV__) {
        if (workInProgress.type !== workInProgress.elementType) {
          const outerPropTypes = type.propTypes;
          if (outerPropTypes) {
            checkPropTypes(
              outerPropTypes,
              resolvedProps, // Resolved for outer only
              'prop',
              getComponentName(type),
              getCurrentFiberStackInDev,
            );
          }
        }
      }
      resolvedProps = resolveDefaultProps(type.type, resolvedProps);
      return updateMemoComponent(
        current,
        workInProgress,
        type,
        resolvedProps,
        updateExpirationTime,
        renderExpirationTime,
      );
    }
    case SimpleMemoComponent: {
      return updateSimpleMemoComponent(
        current,
        workInProgress,
        workInProgress.type,
        workInProgress.pendingProps,
        updateExpirationTime,
        renderExpirationTime,
      );
    }
    case IncompleteClassComponent: {
      const Component = workInProgress.type;
      const unresolvedProps = workInProgress.pendingProps;
      const resolvedProps =
        workInProgress.elementType === Component
          ? unresolvedProps
          : resolveDefaultProps(Component, unresolvedProps);
      return mountIncompleteClassComponent(
        current,
        workInProgress,
        Component,
        resolvedProps,
        renderExpirationTime,
      );
    }
    case SuspenseListComponent: {
      return updateSuspenseListComponent(
        current,
        workInProgress,
        renderExpirationTime,
      );
    }
    case FundamentalComponent: {
      if (enableFundamentalAPI) {
        return updateFundamentalComponent(
          current,
          workInProgress,
          renderExpirationTime,
        );
      }
      break;
    }
    case ScopeComponent: {
      if (enableScopeAPI) {
        return updateScopeComponent(
          current,
          workInProgress,
          renderExpirationTime,
        );
      }
      break;
    }
    case Block: {
      if (enableBlocksAPI) {
        const block = workInProgress.type;
        const props = workInProgress.pendingProps;
        return updateBlock(
          current,
          workInProgress,
          block,
          props,
          renderExpirationTime,
        );
      }
      break;
    }
  }

例如
ClassComponent类型的WIP将创建对应类实例,调用组件的componentWillMount钩子函数,执行组件的render函数。

function updateClassComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps,
  renderExpirationTime: ExpirationTime,
) {
  
  // ...
  const instance = workInProgress.stateNode;
  let shouldUpdate;
  if (instance === null) {
    if (current !== null) {
    // 创建Class组件实例,并调用componentWillMount。
    constructClassInstance(workInProgress, Component, nextProps);
    mountClassInstance(
      workInProgress,
      Component,
      nextProps,
      renderExpirationTime,
    );
    shouldUpdate = true;
  } else if (current === null) {
    // ...
  } else {
    // ...
  }
  const nextUnitOfWork = finishClassComponent(
    current,
    workInProgress,
    Component,
    shouldUpdate,
    hasContext,
    renderExpirationTime,
  );
 // ...
  return nextUnitOfWork;
}

function finishClassComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  shouldUpdate: boolean,
  hasContext: boolean,
  renderExpirationTime: ExpirationTime,
) {
  // ...
  if (
    didCaptureError &&
    typeof Component.getDerivedStateFromError !== 'function'
  ) {
  // ...
  } else {
    if (__DEV__) {
      // ...
    } else {
      // 调用Class组件的render函数,返回其子节点。
      nextChildren = instance.render();
    }
  }

  // React DevTools reads this flag.
  workInProgress.effectTag |= PerformedWork;
  if (current !== null && didCaptureError) {
    // ...
  } else {
    // 调用reconcileChildren生成下一个WIP。
    reconcileChildren(
      current,
      workInProgress,
      nextChildren,
      renderExpirationTime,
    );
  }

 // ...
  return workInProgress.child;
}
  • completeWork过程,对beginWork的中处理的一些压栈处理的逆操作。例如pushHostContext执行popHostContext操作,pushHostContainer执行popHostContainer操作。
    此外,最重要的一点是对HostComponentHostText两种类型的WIP分别创建对应的元素,并将子元素插入当前元素中。
case HostComponent: {
       // ...
        if (wasHydrated) {
          // ...
        } else {
          // 创建对应的元素实例
          let instance = createInstance(
            type,
            newProps,
            rootContainerInstance,
            currentHostContext,
            workInProgress,
          );

          // 将所有子元素插入到当前元素中。
          appendAllChildren(instance, workInProgress, false, false);

          // This needs to be set before we mount Flare event listeners
          workInProgress.stateNode = instance;

          // ...

          // Certain renderers require commit-time effects for initial mount.
          // (eg DOM renderer supports auto-focus for certain elements).
          // Make sure such renderers get scheduled for later work.
          if (
            finalizeInitialChildren(
              instance,
              type,
              newProps,
              rootContainerInstance,
              currentHostContext,
            )
          ) {
            markUpdate(workInProgress);
          }
        }

         //...
      }
      return null;
    }
    case HostText: { // 文本节点
      let newText = newProps;
      if (current && workInProgress.stateNode != null) {
       // ...
      } else {
        if (typeof newText !== 'string') {
          invariant(
            workInProgress.stateNode !== null,
            'We must have new props for new mounts. This error is likely ' +
              'caused by a bug in React. Please file an issue.',
          );
          // This can happen when we abort work.
        }
        const rootContainerInstance = getRootHostContainer();
        const currentHostContext = getHostContext();
        let wasHydrated = popHydrationState(workInProgress);
        if (wasHydrated) {
          if (prepareToHydrateHostTextInstance(workInProgress)) {
            markUpdate(workInProgress);
          }
        } else {
          // 更新对应的文本
          workInProgress.stateNode = createTextInstance(
            newText,
            rootContainerInstance,
            currentHostContext,
            workInProgress,
          );
        }
      }
      return null;
    }

commitWork阶段

在所有节点遍历结束后,初始化过程还将调用finishSyncRender函数,该函数将调用commitRoot函数进入commitWork阶段。

function finishSyncRender(root) {
  // Set this to null to indicate there's no in-progress render.
  workInProgressRoot = null;
  commitRoot(root);
}

commitWork阶段,React将完成以下事情:

  • 执行Function组件的React Effect Hooks
do {
   // ...
   // 改方法中将执行Effect Hooks
    flushPassiveEffects();
  } while (rootWithPendingPassiveEffects !== null);
  • 对Class组件调用componentDidMount,并计算state
function commitLifeCycles(
  finishedRoot: FiberRoot,
  current: Fiber | null,
  finishedWork: Fiber,
  committedExpirationTime: ExpirationTime,
): void {
    // ...
    case ClassComponent: {
      const instance = finishedWork.stateNode;
      if (finishedWork.effectTag & Update) {
        if (current === null) {
          // ...
          // 初始化阶段调用`componentDidMount`
          instance.componentDidMount();
          stopPhaseTimer();
        } else {
          // ...
          // 组件更新阶段调用`componentDidUpdate`
          instance.componentDidUpdate(
            prevProps,
            prevState,
            instance.__reactInternalSnapshotBeforeUpdate,
          );
          stopPhaseTimer();
        }
      }
      const updateQueue = finishedWork.updateQueue;
      if (updateQueue !== null) {
        // 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.
        commitUpdateQueue(finishedWork, updateQueue, instance);
      }
      return;
    }
  }
 // ...
}

...等等。

相关文章

网友评论

      本文标题:react-reconsiler模块 - React源码解析(一

      本文链接:https://www.haomeiwen.com/subject/yaqjyhtx.html