React源码学习进阶(三)rootFiber的创建流程

本文采用React v16.13.1版本源码进行分析

源码解析

我们调用ReactDOM.render方法进行渲染,其实在Fiber架构下是同步渲染模式,它的入口代码在packages/react-dom/src/client/ReactDOMLegacy.js(从命名上可以看出来,React后续会淘汰这种渲染模式,终于在18版本中默认采用了concurrent):

export function render(
  element: React$Element<any>,
  container: Container,
  callback: ?Function,
) {
  return legacyRenderSubtreeIntoContainer(
    null,
    element,
    container,
    false,
    callback,
  );
}

这个方法调用的是legacyRenderSubtreeIntoContainer方法:

function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  container: Container,
  forceHydrate: boolean,
  callback: ?Function,
) {
  let root: RootType = (container._reactRootContainer: any);
  let fiberRoot;
  if (!root) {
    // Initial mount
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );
    fiberRoot = root._internalRoot;
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // Initial mount should not be batched.
    unbatchedUpdates(() => {
      updateContainer(children, fiberRoot, parentComponent, callback);
    });
  } else {
    fiberRoot = root._internalRoot;
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // Update
    updateContainer(children, fiberRoot, parentComponent, callback);
  }
  return getPublicRootInstance(fiberRoot);
}

这个方法的逻辑很清晰,对于初次挂载的场景,它主要做了几件事情:

  1. 调用legacyCreateRootFromDOMContainer创建root节点

  2. 调用updateContainer启动整个更新流程(挂载流程)

  3. 返回root节点的instance,这个逻辑可以不用关注,基本上我们不会使用这个返回值来做什么。

root节点的创建流程

接下来我们看一下root节点是怎样被创建出来的,主要入口在legacyCreateRootFromDOMContainer中:

这个函数逻辑其实非常简单,清理掉之前挂载过的内容后,调用createLegacyRoot方法。这个方法位于packages/react-dom/src/client/ReactDOMRoot.js中:

再看看ReactDOMBlockingRoot

可以看到构造方法里面其实就挂了一个成员变量_internalRoot,这个值也是刚刚在外面判断用的,具体root代码生成是在createRootImpl里:

这里核心就做了两件事:

  • 调用createContainer创建root

  • 调用markContainerAsRoot为container打上root标记

对于第二点只是在DOM节点上挂了一个引用,没什么好说的,重点来看看root的实现,位于packages/react-reconciler/src/ReactFiberReconciler.js

最终它会通过createFiberRoot,位于packages/react-reconciler/src/ReactFiberRoot.js

注意这里的几个参数,containerInfo实际上就是DOM节点本身,tagLegacyRoot,接下来核心就是做了三件事:

  • 实例化root节点,构造函数是FiberRootNode

  • 创建一个rootFiber,通过createHostRootFiber,并通过currentstateNode双向引用

  • 初始化updateQueue

首先看一下root的构造函数:

这里大多数参数目前不需要关注,只需要关注root节点自己通过currentstateNode双向链接起来了:

image-20220914220816376

接下来我们看一下createHostRootFiber的逻辑,源码位于packages/react-reconciler/src/ReactFiber.js

实际上这段代码就是计算一个mode值,然后调用createFiber创建一个Fiber返回,实际上对于ReactDOM.render来说,这里mode计算出来就是默认的NoMode

FiberNode的构造函数是:

其中Fiber的数据结构上篇文章已经讲过,这里不再赘述。

要注意的是这里的stateNode在外面被指向了root

最后我们来看一下initializeUpdateQueue的逻辑,源码位于packages/react-reconciler/src/ReactUpdateQueue.js

可以看到它就是一个初始的queue结构,而这个结构将用于服务后续我们更新state的流程。

至此,我们的root节点就已经创建完毕了。

小结一下

实际上rootrootFiber的创建流程还是比较简单的,它们在创建过程中主要就做了三件事:

image-20220914222253261

我们需要注意的是rootrootFiber本身的数据结构,以及它们作为双向链表的起点自身的链接关系:

image-20220914223300841

整个root结构实际上是挂载和更新的起点,React Fiber通过整个root结构的创建和更新来完成后续整个更新操作链路。

Last updated