ReactDOM.render
render(
element: React$Element<any>,
container: DOMContainer,
callback: ?Function,
) {
// 注意下 forceHydrate 参数(也就是第四个参数),为 true 时是服务端渲染
// 调用 render 函数的话这个值永远为 false,调用 hydrate 函数的话这个值会为 true
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
接下来是legacyRenderSubtreeIntoContainer
没有root节点,创建一个新的
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
container: DOMContainer,
forceHydrate: boolean,
callback: ?Function,
) {
// 一开始进来 container 上是肯定没有这个属性的
let root: Root = (container._reactRootContainer: any);
// 没有 root 会执行 if 中的操作
if (!root) {
// Initial mount
// 创建一个 root 出来,类型是 ReactRoot,并且挂载到container._reactRootContainer上
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate,
);
}
}
接下来是legacyCreateRootFromDOMContainer
返回ReactRoot的实例
function legacyCreateRootFromDOMContainer(
container: DOMContainer,
forceHydrate: boolean,
): Root {
const shouldHydrate =
forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
// forceHydrate为false,所以进if的判断
if (!shouldHydrate) {
let warned = false;
let rootSibling;
// container 内部如果有元素的话,就全部清掉
// 但是一般来说我们都是这样写 container 的: <div id='root'></div>
// 所以说 container 内部不要写任何的节点,一是会被清掉,二是还要进行 DOM 操作,可能还会涉及到重绘回流等等
while ((rootSibling = container.lastChild)) {
container.removeChild(rootSibling);
}
}
// Legacy roots are not async by default.
// 对于 Root 来说不需要异步
const isConcurrent = false;
return new ReactRoot(container, isConcurrent, shouldHydrate);
}
ReactRoot
返回创建FiberRoot方法,并且ReactRoot实例通过_internalRoot关联
function ReactRoot(
container: DOMContainer,
isConcurrent: boolean,
hydrate: boolean,
) {
// 这个 root 指的是 FiberRoot
const root = createContainer(container, isConcurrent, hydrate);
this._internalRoot = root;
}
function createContainer(
containerInfo: Container,
isConcurrent: boolean,
hydrate: boolean,
): OpaqueRoot {
return createFiberRoot(containerInfo, isConcurrent, hydrate);
}
createFiberRoot
创建FiberRoot节点和RootFiber,他们之间相互引用,对于 FiberRoot 对象来说,我们现在只需要了解两个属性,分别是 containerInfo 及 current。前者代表着容器信息,也就是我们的 document.querySelector('#root');后者指向 RootFiber
对于 RootFiber 对象来说,我们需要了解的属性稍微多点.return、child、sibling 这三个属性很重要,它们是构成 fiber 树的主体数据结构。fiber 树其实是一个单链表树结构,return 及 child 分别对应着树的父子节点,并且父节点只有一个 child 指向它的第一个子节点,即便是父节点有好多个子节点。那么多个子节点如何连接起来呢?答案是 sibling,每个子节点都有一个 sibling 属性指向着下一个子节点,都有一个 return 属性指向着父节点。
最后是 alternate 属性。其实在一个 React 应用中,通常来说都有两个 fiebr 树,一个叫做 old tree,另一个叫做 workInProgress tree。前者对应着已经渲染好的 DOM 树,后者是正在执行更新中的 fiber tree,还能便于中断后恢复。两棵树的节点互相引用,便于共享一些内部的属性,减少内存的开销(double buffering)。
function createFiberRoot(
containerInfo: any,
isConcurrent: boolean,
hydrate: boolean,
): FiberRoot {
// FiberRootNode 内部创建了很多属性
const root: FiberRoot = (new FiberRootNode(containerInfo, hydrate): any);
// Cyclic construction. This cheats the type system right now because
// stateNode is any.
// 创建一个 root fiber,这也是 React 16 中的核心架构了
// fiber 其实也会组成一个树结构,内部使用了单链表树结构,每个节点及组件都会对应一个 fiber
// FiberRoot 和 Root Fiber 会互相引用
// 另外如果你有 React 写的项目的话,可以通过以下代码找到 Fiber Root,它对应着容器
// document.querySelector('#root')._reactRootContainer._internalRoot
const uninitializedFiber = createHostRootFiber(isConcurrent);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
return root;
}
Fiber树图解