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);
}这个方法的逻辑很清晰,对于初次挂载的场景,它主要做了几件事情:
调用
legacyCreateRootFromDOMContainer创建root节点调用
updateContainer启动整个更新流程(挂载流程)返回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节点本身,tag是LegacyRoot,接下来核心就是做了三件事:
实例化root节点,构造函数是
FiberRootNode创建一个
rootFiber,通过createHostRootFiber,并通过current和stateNode双向引用初始化
updateQueue
首先看一下root的构造函数:
这里大多数参数目前不需要关注,只需要关注root节点自己通过current和stateNode双向链接起来了:

接下来我们看一下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节点就已经创建完毕了。
小结一下
实际上root和rootFiber的创建流程还是比较简单的,它们在创建过程中主要就做了三件事:

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

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