1、初始化渲染
要将 React 元素渲染到页面中,分为两个阶段,render 阶段和 commit 阶段。
- render 阶段负责创建 Fiber 数据结构并为 Fiber 节点打标记,标记当前 Fiber 节点要进行的 DOM 操作。
- commit 阶段负责根据 Fiber 节点标记 ( effectTag ) 进行相应的 DOM 操作。
2、render阶段
2.1 reactDom.render()
文件位置:packages/react-dom/src/client/ReactDOMLegacy.js
/*** 渲染入口* element 要进行渲染的 ReactElement, createElement 方法的返回值* container 渲染容器 <div id="root"></div>* callback 渲染完成后执行的回调函数*/export function render(element: React$Element<any>,container: Container,callback: ?Function,) {// 检测 container 是否是符合要求的渲染容器// 即检测 container 是否是真实的DOM对象// 如果不符合要求就报错invariant(isValidContainer(container),'Target container is not a DOM element.',);return legacyRenderSubtreeIntoContainer(// 父组件 初始渲染没有父组件 传递 null 占位null,element,container,// 是否为服务器端渲染 false 不是服务器端渲染 true 是服务器端渲染false,callback,);}
2.2 isValidContainer(不太重要)
文件位置:packages/react-dom/src/client/ReactDOMRoot.js
/*** 判断 node 是否是符合要求的 DOM 节点* 1. node 可以是元素节点* 2. node 可以是 document 节点* 3. node 可以是 文档碎片节点* 4. node 可以是注释节点但注释内容必须是 react-mount-point-unstable* react 内部会找到注释节点的父级 通过调用父级元素的 insertBefore 方法, 将 element 插入到注释节点的前面*/export function isValidContainer(node: mixed): boolean {return !!(node &&(node.nodeType === ELEMENT_NODE ||node.nodeType === DOCUMENT_NODE ||node.nodeType === DOCUMENT_FRAGMENT_NODE ||(node.nodeType === COMMENT_NODE &&(node: any).nodeValue === ' react-mount-point-unstable ')));}
2.3 初始化FiberRoot
下面的一大串代码也就是干了这几件事,主要是查找到createFiberRoot方法;
注意:标记绿色的方法可以不用看,主要是一步步的调用;
- 创建 FiberRoot 和 rootFiber
- 为 fiberRoot 添加 current 属性 值为 rootFiber
- 为 rootFiber 添加 stateNode 属性 值为 fiberRoot

2.3.1 legacyRenderSubtreeIntoContainer(本段代码比较重要)
文件位置: packages/react-dom/src/client/ReactDOMLegacy.js
/*** 将子树渲染到容器中 (初始化 Fiber 数据结构: 创建 fiberRoot 及 rootFiber)* parentComponent: 父组件, 初始渲染传入了 null* children: render 方法中的第一个参数, 要渲染的 ReactElement* container: 渲染容器* forceHydrate: true 为服务端渲染, false 为客户端渲染* callback: 组件渲染完成后需要执行的回调函数**/function legacyRenderSubtreeIntoContainer(parentComponent: ?React$Component<any, any>,children: ReactNodeList,container: Container,forceHydrate: boolean,callback: ?Function,) {/*** 检测 container 是否已经是初始化过的渲染容器* react 在初始渲染时会为最外层容器添加 _reactRootContainer 属性* react 会根据此属性进行不同的渲染方式* root 不存在 表示初始渲染* root 存在 表示更新*/// 获取 container 容器对象下是否有 _reactRootContainer 属性let root: RootType = (container._reactRootContainer: any);// 即将存储根 Fiber 对象let fiberRoot;if (!root) {// 初始渲染// 初始化根 Fiber 数据结构// 为 container 容器添加 _reactRootContainer 属性// 在 _reactRootContainer 对象中有一个属性叫做 _internalRoot// _internalRoot 属性值即为 FiberRoot 表示根节点 Fiber 数据结构// legacyCreateRootFromDOMContainer// createLegacyRoot// new ReactDOMBlockingRoot -> this._internalRoot// createRootImplroot = container._reactRootContainer = legacyCreateRootFromDOMContainer(container,forceHydrate,);// 获取 Fiber Root 对象fiberRoot = root._internalRoot;/*** 改变 callback 函数中的 this 指向* 使其指向 render 方法第一个参数的真实 DOM 对象*/// 如果 callback 参数是函数类型if (typeof callback === 'function') {// 使用 originalCallback 存储 callback 函数const originalCallback = callback;// 为 callback 参数重新赋值callback = function () {// 获取 render 方法第一个参数的真实 DOM 对象// 实际上就是 id="root" 的 div 的子元素// rootFiber.child.stateNode// rootFiber 就是 id="root" 的 divconst instance = getPublicRootInstance(fiberRoot);// 调用 callback 函数并改变函数内部 this 指向originalCallback.call(instance);};}// 初始化渲染不执行批量更新// 因为批量更新是异步的是可以被打断的, 但是初始化渲染应该尽快完成不能被打断// 所以不执行批量更新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);};}// UpdateupdateContainer(children, fiberRoot, parentComponent, callback);}// 返回 render 方法第一个参数的真实 DOM 对象作为 render 方法的返回值// 就是说渲染谁 返回谁的真实 DOM 对象return getPublicRootInstance(fiberRoot);}
2.3.2 legacyCreateRootFromDOMContainer
文件位置: packages/react-dom/src/client/ReactDOMLegacy.js
/*** 判断是否为服务器端渲染 如果不是服务器端渲染* 清空 container 容器中的节点*/function legacyCreateRootFromDOMContainer(container: Container,forceHydrate: boolean,): RootType {// container => <div id="root"></div>// 检测是否为服务器端渲染const shouldHydrate =forceHydrate || shouldHydrateDueToLegacyHeuristic(container);// 如果不是服务器端渲染if (!shouldHydrate) {let rootSibling;// 开启循环 删除 container 容器中的节点while ((rootSibling = container.lastChild)) {// 删除 container 容器中的节点container.removeChild(rootSibling);/*** 为什么要清除 container 中的元素 ?* 为提供首屏加载的用户体验, 有时需要在 container 中放置一些占位图或者 loading 图* 就无可避免的要向 container 中加入 html 标记.* 在将 ReactElement 渲染到 container 之前, 必然要先清空 container* 因为占位图和 ReactElement 不能同时显示** 在加入占位代码时, 最好只有一个父级元素, 可以减少内部代码的循环次数以提高性能* <div>* <p>placement<p>* <p>placement<p>* <p>placement<p>* </div>*/}}return createLegacyRoot(container,shouldHydrate? {hydrate: true,}: undefined,);}
2.3.3 createLegacyRoot
文件位置: packages/react-dom/src/client/ReactDOMRoot.js
/*** 通过实例化 ReactDOMBlockingRoot 类创建 LegacyRoot*/export function createLegacyRoot(container: Container,options?: RootOptions,): RootType {// container => <div id="root"></div>// LegacyRoot 常量, 值为 0,// 通过 render 方法创建的 container 就是 LegacyRootreturn new ReactDOMBlockingRoot(container, LegacyRoot, options);}
2.3.4 ReactDOMBlockingRoot
文件位置: packages/react-dom/src/client/ReactDOMRoot.js
/*** 类, 通过它可以创建 LegacyRoot 的 Fiber 数据结构*/function ReactDOMBlockingRoot(container: Container,tag: RootTag,options: void | RootOptions,) {// tag => 0 => legacyRoot// container => <div id="root"></div>// container._reactRootContainer = {_internalRoot: {}}this._internalRoot = createRootImpl(container, tag, options);}
2.3.5 createRootImpl
文件位置: packages/react-dom/src/client/ReactDOMRoot.js
function createRootImpl(container: Container,tag: RootTag,options: void | RootOptions,) {// container => <div id="root"></div>// tag => 0// options => undefinedconst root = createContainer(container, tag, hydrate, hydrationCallbacks);markContainerAsRoot(root.current, container);return root;}
2.3.6 createContainer
文件位置: packages/react-reconciler/src/ReactFiberReconciler.js
// 创建 containerexport function createContainer(containerInfo: Container,tag: RootTag,hydrate: boolean,hydrationCallbacks: null | SuspenseHydrationCallbacks,): OpaqueRoot {// containerInfo => <div id="root"></div>// tag: 0// hydrate: false// hydrationCallbacks: null// 忽略了和服务器端渲染相关的内容return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);}
2.3.7 createFiberRoot(本段代码是重点)
文件位置: packages/react-reconciler/src/ReactFiberRoot.js
// 创建根节点对应的 fiber 对象export function createFiberRoot(containerInfo: any,tag: RootTag,hydrate: boolean,hydrationCallbacks: null | SuspenseHydrationCallbacks,): FiberRoot {// 创建 FiberRootconst root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);// 创建根节点对应的 rootFiberconst uninitializedFiber = createHostRootFiber(tag);// 为 fiberRoot 添加 current 属性 值为 rootFiberroot.current = uninitializedFiber;// 为 rootFiber 添加 stateNode 属性 值为 fiberRootuninitializedFiber.stateNode = root;// 为 fiber 对象添加 updateQueue 属性, 初始化 updateQueue 对象// updateQueue 用于存放 Update 对象// Update 对象用于记录组件状态的改变initializeUpdateQueue(uninitializedFiber);// 返回 rootreturn root;}
2.3.8 initializeUpdateQueue
文件位置: packages/react-reconciler/src/ReactFiberRoot.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;}
