经过五章的学习,我们终于回到了React应用的起点。
这一节我们完整的走通ReactDOM.render完成页面渲染的整个流程。
创建fiber
从双缓存机制一节我们知道,首次执行ReactDOM.render会创建fiberRootNode和rootFiber。其中fiberRootNode是整个应用的根节点,rootFiber是要渲染组件所在组件树的根节点。
这一步发生在调用ReactDOM.render后进入的legacyRenderSubtreeIntoContainer方法中。
// container指ReactDOM.render的第二个参数(即应用挂载的DOM节点)root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container,forceHydrate,);fiberRoot = root._internalRoot;
你可以从这里
(opens new window)看到这一步的代码
legacyCreateRootFromDOMContainer方法内部会调用createFiberRoot方法完成fiberRootNode和rootFiber的创建以及关联。并初始化updateQueue。
export function createFiberRoot(containerInfo: any,tag: RootTag,hydrate: boolean,hydrationCallbacks: null | SuspenseHydrationCallbacks,): FiberRoot {// 创建fiberRootNodeconst root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);// 创建rootFiberconst uninitializedFiber = createHostRootFiber(tag);// 连接rootFiber与fiberRootNoderoot.current = uninitializedFiber;uninitializedFiber.stateNode = root;// 初始化updateQueueinitializeUpdateQueue(uninitializedFiber);return root;}
根据以上代码,现在我们可以在双缓存机制一节基础上补充上rootFiber到fiberRootNode的引用。

你可以从这里
(opens new window)看到这一步的代码
创建update
我们已经做好了组件的初始化工作,接下来就等待创建Update来开启一次更新。
这一步发生在updateContainer方法中。
export function updateContainer(element: ReactNodeList,container: OpaqueRoot,parentComponent: ?React$Component<any, any>,callback: ?Function,): Lane {// ...省略与逻辑不相关代码// 创建updateconst update = createUpdate(eventTime, lane, suspenseConfig);// update.payload为需要挂载在根节点的组件update.payload = {element};// callback为ReactDOM.render的第三个参数 —— 回调函数callback = callback === undefined ? null : callback;if (callback !== null) {update.callback = callback;}// 将生成的update加入updateQueueenqueueUpdate(current, update);// 调度更新scheduleUpdateOnFiber(current, lane, eventTime);// ...省略与逻辑不相关代码}
你可以从这里
(opens new window)看到
updateContainer的代码
值得注意的是其中update.payload = {element};
这就是我们在Update一节介绍的,对于HostRoot,payload为ReactDOM.render的第一个传参。
流程概览
至此,ReactDOM.render的流程就和我们已知的流程连接上了。
整个流程如下:
创建fiberRootNode、rootFiber、updateQueue(`legacyCreateRootFromDOMContainer`)||v创建Update对象(`updateContainer`)||v从fiber到root(`markUpdateLaneFromFiberToRoot`)||v调度更新(`ensureRootIsScheduled`)||vrender阶段(`performSyncWorkOnRoot` 或 `performConcurrentWorkOnRoot`)||vcommit阶段(`commitRoot`)
React的其他入口函数
当前React共有三种模式:
legacy,这是当前React使用的方式。当前没有计划删除本模式,但是这个模式可能不支持一些新功能。blocking,开启部分concurrent模式特性的中间模式。目前正在实验中。作为迁移到concurrent模式的第一个步骤。concurrent,面向未来的开发模式。我们之前讲的任务中断/任务优先级都是针对concurrent模式。
你可以从下表看出各种模式对特性的支持:
| legacy 模式 | blocking 模式 | concurrent 模式 | |
|---|---|---|---|
String Refs (opens new window) | ✅ | 🚫 | 🚫 |
Legacy Context (opens new window) | ✅ | 🚫 | 🚫 |
findDOMNode (opens new window) | ✅ | 🚫 | 🚫 |
Suspense (opens new window) | ✅ | ✅ | ✅ |
SuspenseList (opens new window) | 🚫 | ✅ | ✅ |
| Suspense SSR + Hydration | 🚫 | ✅ | ✅ |
| Progressive Hydration | 🚫 | ✅ | ✅ |
| Selective Hydration | 🚫 | 🚫 | ✅ |
| Cooperative Multitasking | 🚫 | 🚫 | ✅ |
| Automatic batching of multiple setStates | 🚫* | ✅ | ✅ |
Priority-based Rendering (opens new window) | 🚫 | 🚫 | ✅ |
Interruptible Prerendering (opens new window) | 🚫 | 🚫 | ✅ |
useTransition (opens new window) | 🚫 | 🚫 | ✅ |
useDeferredValue (opens new window) | 🚫 | 🚫 | ✅ |
Suspense Reveal “Train” (opens new window) | 🚫 | 🚫 | ✅ |
*:legacy模式在合成事件中有自动批处理的功能,但仅限于一个浏览器任务。非React事件想使用这个功能必须使用 unstable_batchedUpdates。在blocking模式和concurrent模式下,所有的setState在默认情况下都是批处理的。
**:会在开发中发出警告。
模式的变化影响整个应用的工作方式,所以无法只针对某个组件开启不同模式。
基于此原因,可以通过不同的入口函数开启不同模式:
legacy—ReactDOM.render(<App />, rootNode)blocking—ReactDOM.createBlockingRoot(rootNode).render(<App />)concurrent—ReactDOM.createRoot(rootNode).render(<App />)
你可以在这里
(opens new window)看到
React团队解释为什么会有这么多模式
虽然不同模式的入口函数不同,但是他们仅对fiber.mode变量产生影响,对我们在流程概览中描述的流程并无影响。
