# React源码学习进阶（三）rootFiber的创建流程

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

### 源码解析

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

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

这个方法调用的是`legacyRenderSubtreeIntoContainer`方法：

```js
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`中：

```js
function legacyCreateRootFromDOMContainer(
  container: Container,
  forceHydrate: boolean,
): RootType {
  const shouldHydrate =
    forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
  // First clear any existing content.
  if (!shouldHydrate) {
    let warned = false;
    let rootSibling;
    while ((rootSibling = container.lastChild)) {
      container.removeChild(rootSibling);
    }
  }

  return createLegacyRoot(
    container,
    shouldHydrate
      ? {
          hydrate: true,
        }
      : undefined,
  );
}
```

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

```js
export function createLegacyRoot(
  container: Container,
  options?: RootOptions,
): RootType {
  return new ReactDOMBlockingRoot(container, LegacyRoot, options);
}
```

再看看`ReactDOMBlockingRoot`：

```js
function ReactDOMBlockingRoot(
  container: Container,
  tag: RootTag,
  options: void | RootOptions,
) {
  this._internalRoot = createRootImpl(container, tag, options);
}
```

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

```js
function createRootImpl(
  container: Container,
  tag: RootTag,
  options: void | RootOptions,
) {
  // Tag is either LegacyRoot or Concurrent Root
  const hydrate = options != null && options.hydrate === true;
  const hydrationCallbacks =
    (options != null && options.hydrationOptions) || null;
  const root = createContainer(container, tag, hydrate, hydrationCallbacks);
  markContainerAsRoot(root.current, container);
  if (hydrate && tag !== LegacyRoot) {
    const doc =
      container.nodeType === DOCUMENT_NODE
        ? container
        : container.ownerDocument;
    eagerlyTrapReplayableEvents(container, doc);
  }
  return root;
}
```

这里核心就做了两件事：

* 调用`createContainer`创建root
* 调用`markContainerAsRoot`为container打上root标记

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

```js
export function createContainer(
  containerInfo: Container,
  tag: RootTag,
  hydrate: boolean,
  hydrationCallbacks: null | SuspenseHydrationCallbacks,
): OpaqueRoot {
  return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);
}
```

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

```js
export function createFiberRoot(
  containerInfo: any,
  tag: RootTag,
  hydrate: boolean,
  hydrationCallbacks: null | SuspenseHydrationCallbacks,
): 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.
  const uninitializedFiber = createHostRootFiber(tag);
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;

  initializeUpdateQueue(uninitializedFiber);

  return root;
}
```

注意这里的几个参数，`containerInfo`实际上就是DOM节点本身，`tag`是`LegacyRoot`，接下来核心就是做了三件事：

* 实例化root节点，构造函数是`FiberRootNode`
* 创建一个`rootFiber`，通过`createHostRootFiber`，并通过`current`和`stateNode`双向引用
* 初始化`updateQueue`

首先看一下`root`的构造函数：

```js
function FiberRootNode(containerInfo, tag, hydrate) {
  this.tag = tag;
  this.current = null;
  this.containerInfo = containerInfo;
  this.pendingChildren = null;
  this.pingCache = null;
  this.finishedExpirationTime = NoWork;
  this.finishedWork = null;
  this.timeoutHandle = noTimeout;
  this.context = null;
  this.pendingContext = null;
  this.hydrate = hydrate;
  this.callbackNode = null;
  this.callbackPriority = NoPriority;
  this.firstPendingTime = NoWork;
  this.firstSuspendedTime = NoWork;
  this.lastSuspendedTime = NoWork;
  this.nextKnownPendingLevel = NoWork;
  this.lastPingedTime = NoWork;
  this.lastExpiredTime = NoWork;

  if (enableSchedulerTracing) {
    this.interactionThreadID = unstable_getThreadID();
    this.memoizedInteractions = new Set();
    this.pendingInteractionMap = new Map();
  }
  if (enableSuspenseCallback) {
    this.hydrationCallbacks = null;
  }
}
```

这里大多数参数目前不需要关注，只需要关注`root`节点自己通过`current`和`stateNode`双向链接起来了：

![image-20220914220816376](https://tva1.sinaimg.cn/large/e6c9d24egy1h66hkxbpj1j20lc06wjri.jpg)

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

```js
export function createHostRootFiber(tag: RootTag): Fiber {
  let mode;
  if (tag === ConcurrentRoot) {
    mode = ConcurrentMode | BlockingMode | StrictMode;
  } else if (tag === BlockingRoot) {
    mode = BlockingMode | StrictMode;
  } else {
    mode = NoMode;
  }

  if (enableProfilerTimer && isDevToolsPresent) {
    // Always collect profile timings when DevTools are present.
    // This enables DevTools to start capturing timing at any point–
    // Without some nodes in the tree having empty base times.
    mode |= ProfileMode;
  }

  return createFiber(HostRoot, null, null, mode);
}
```

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

```js
const createFiber = function(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
): Fiber {
  // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
  return new FiberNode(tag, pendingProps, key, mode);
};
```

而`FiberNode`的构造函数是：

```js
function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // Instance
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null;

  // Fiber
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.dependencies = null;

  this.mode = mode;

  // Effects
  this.effectTag = NoEffect;
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  this.expirationTime = NoWork;
  this.childExpirationTime = NoWork;

  this.alternate = null;

  if (enableProfilerTimer) {
    // Note: The following is done to avoid a v8 performance cliff.
    //
    // Initializing the fields below to smis and later updating them with
    // double values will cause Fibers to end up having separate shapes.
    // This behavior/bug has something to do with Object.preventExtension().
    // Fortunately this only impacts DEV builds.
    // Unfortunately it makes React unusably slow for some applications.
    // To work around this, initialize the fields below with doubles.
    //
    // Learn more about this here:
    // https://github.com/facebook/react/issues/14365
    // https://bugs.chromium.org/p/v8/issues/detail?id=8538
    this.actualDuration = Number.NaN;
    this.actualStartTime = Number.NaN;
    this.selfBaseDuration = Number.NaN;
    this.treeBaseDuration = Number.NaN;

    // It's okay to replace the initial doubles with smis after initialization.
    // This won't trigger the performance cliff mentioned above,
    // and it simplifies other profiler code (including DevTools).
    this.actualDuration = 0;
    this.actualStartTime = -1;
    this.selfBaseDuration = 0;
    this.treeBaseDuration = 0;
  }

  // This is normally DEV-only except www when it adds listeners.
  // TODO: remove the User Timing integration in favor of Root Events.
  if (enableUserTimingAPI) {
    this._debugID = debugCounter++;
    this._debugIsCurrentlyTiming = false;
  }
}
```

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

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

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

```js
export function initializeUpdateQueue<State>(fiber: Fiber): void {
  const queue: UpdateQueue<State> = {
    baseState: fiber.memoizedState,
    baseQueue: null,
    shared: {
      pending: null,
    },
    effects: null,
  };
  fiber.updateQueue = queue;
}
```

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

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

### 小结一下

实际上`root`和`rootFiber`的创建流程还是比较简单的，它们在创建过程中主要就做了三件事：

![image-20220914222253261](https://tva1.sinaimg.cn/large/e6c9d24egy1h66i03ifuvj217a0m675l.jpg)

我们需要注意的是`root`和`rootFiber`本身的数据结构，以及它们作为双向链表的起点自身的链接关系：

![image-20220914223300841](https://tva1.sinaimg.cn/large/e6c9d24egy1h66iampdn2j21t40m8q85.jpg)

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://meng-jian.gitbook.io/react-yuan-ma-quan-jie/react-yuan-ma-xue-xi-jin-jie-san-rootfiber-de-chuang-jian-liu-cheng.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
