React 应用从启动到渲染成页面的过程涉及一套完整的流程,涉及初始化、编译、协调(Diff)、提交等多个阶段,核心目标是将 JSX 描述的组件树高效转换为真实 DOM 并渲染到页面。以下是详细流程拆解:
- 编译准备:JSX 被编译为
createElement调用,入口文件定义根组件和挂载点。- 初始化启动:调用
root.render(),创建 Fiber 根节点,触发根组件渲染,生成虚拟 DOM 树。- 协调阶段:首次渲染时全量创建 Fiber 节点(更新时执行 Diff 找差异),通过 Fiber 调度拆分任务,避免主线程阻塞。
- 提交阶段:执行 DOM 插入/修改/删除操作,触发生命周期/副作用,最终浏览器渲染页面。
一、启动前的准备:代码编译与环境初始化
在应用真正运行前,需要先完成代码转换和环境配置,为运行时渲染做准备。
JSX 编译为虚拟 DOM 函数
React 应用中编写的JSX(如<div><span>Hello</span></div>)并非原生 JavaScript,需要通过 Babel 等编译器 转换为React.createElement调用(或 React 17+ 的jsx函数)。
示例:jsx// 源代码 <div className="app">Hello</div> // 编译后(简化) React.createElement('div', { className: 'app' }, 'Hello')该函数执行后会生成 虚拟 DOM 对象(描述节点类型、属性、子节点的 JavaScript 对象),避免直接操作真实 DOM 的性能开销。
入口文件与根组件定义
应用通过入口文件(通常是index.js)指定根组件和挂载点,例如:javascriptimport React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; // 获取页面中的根容器(真实 DOM 节点) const root = ReactDOM.createRoot(document.getElementById('root')); // 挂载根组件 App root.render(<App />);
二、启动阶段:初始化与首次渲染触发
当 root.render(<App />) 执行时,React 正式启动渲染流程,核心是将根组件转换为可渲染的虚拟 DOM 树。
创建 Fiber 根节点
React 会初始化一个 Fiber 根节点(FiberRoot),作为整个应用的“调度中心”。Fiber 是 React 16+ 引入的工作单元,每个节点对应一个组件或 DOM 元素,存储了节点类型、属性、子节点指针等信息,用于后续的任务拆分和调度。根组件渲染(生成虚拟 DOM 树)
React 会执行根组件App的渲染逻辑(函数组件执行函数体,类组件执行render方法),递归渲染其所有子组件,最终生成一棵完整的 虚拟 DOM 树(以 Fiber 节点链表的形式存储)。
例如,App组件若包含Header和Content子组件,会依次渲染这两个组件,拼接成完整的虚拟 DOM 结构。
三、协调阶段(Reconciliation):Diff 算法与任务调度
协调阶段是 React 性能优化的核心,目标是确定虚拟 DOM 树中需要更新的部分,且该过程可被中断(由 Fiber 架构支持)。
首次渲染的“协调”:全量创建 Fiber 节点
首次渲染时,由于没有旧的虚拟 DOM 树可供对比,React 会直接根据新虚拟 DOM 树 创建所有 Fiber 节点,并标记为“需要插入到真实 DOM”的状态。
(注:后续更新时,此阶段才会执行 Diff 算法对比新旧 Fiber 树,找出差异)Fiber 调度(Scheduler)
React 通过 调度器(Scheduler) 管理协调阶段的任务:- 将 Fiber 树的创建/对比拆分为多个小任务(每个任务处理一个 Fiber 节点);
- 每次只执行一个小任务(约 5ms),执行完后检查浏览器是否有空闲时间(通过
requestIdleCallback模拟); - 若有高优先级任务(如用户输入、动画),则暂停当前任务,优先处理高优先级任务,避免页面卡顿。
四、提交阶段(Commit):真实 DOM 操作与页面渲染
协调阶段完成后,React 进入不可中断的提交阶段,将协调阶段计算出的差异应用到真实 DOM,并触发生命周期/副作用。
执行 DOM 操作
React 遍历 Fiber 树中标记为“需要更新”的节点(首次渲染时所有节点均需插入),执行对应的 DOM 操作:- 新增节点:调用
document.createElement创建真实 DOM 元素,设置属性(className、style等),并插入到父节点中; - (更新时)修改节点:只更新变化的属性(如
onClick事件、src等),避免全量重设; - (更新时)删除节点:调用
removeChild移除不再需要的真实 DOM 元素。
- 新增节点:调用
触发生命周期与副作用
- 类组件:触发
componentDidMount(首次渲染)、componentDidUpdate(更新)等生命周期方法; - 函数组件:执行
useEffect、useLayoutEffect等 Hook 的副作用(useLayoutEffect在 DOM 操作后同步执行,useEffect在浏览器绘制后异步执行)。
- 类组件:触发
完成渲染,页面可见
当所有 DOM 操作执行完毕,浏览器会触发重排(Reflow)和重绘(Repaint),将更新后的 DOM 树渲染到页面上,用户即可看到最终的界面。
五、总结:从启动到渲染的完整流程
- 编译准备:JSX 被编译为
createElement调用,入口文件定义根组件和挂载点。 - 初始化启动:调用
root.render(),创建 Fiber 根节点,触发根组件渲染,生成虚拟 DOM 树。 - 协调阶段:首次渲染时全量创建 Fiber 节点(更新时执行 Diff 找差异),通过 Fiber 调度拆分任务,避免主线程阻塞。
- 提交阶段:执行 DOM 插入/修改/删除操作,触发生命周期/副作用,最终浏览器渲染页面。
整个流程中,Fiber 架构的任务拆分、调度器的优先级管理,以及 Diff 算法的高效对比,共同保证了 React 应用在复杂场景下的渲染性能和用户体验。