一、main.jsx 的核心作用
- 应用执行入口:它是整个 React 应用程序的 JavaScript 执行起点,负责启动整个项目。
- 挂载根组件 :它负责找到 HTML 页面中的特定容器(通常是
id="root"的<div>),并将 React 的根组件(通常是<App />)渲染到这个容器中。 - 全局配置初始化:在实际开发中,它通常被用来配置全局的上下文(Context)、引入全局样式(如 CSS 重置文件)、配置路由(React Router)或全局主题(如 Material UI 的 ThemeProvider)。
二、代码示例
jsx
// 1. 引入 React 核心库及创建根节点的方法
import React from 'react';
import ReactDOM from 'react-dom/client';
// 2. 引入全局样式文件(通常用于重置浏览器默认样式)
import './index.css';
// 3. 引入应用的根组件
import App from './App.jsx';
// 4. 引入路由相关组件(实际开发中通常在此处配置路由)
import { BrowserRouter } from 'react-router-dom';
// 5. 找到 HTML 中的 root 节点,创建 React 根实例并渲染
ReactDOM.createRoot(document.getElementById('root')).render(
// StrictMode 是 React 的严格模式,仅在开发环境下运行,用于帮助检测潜在的代码问题
<React.StrictMode>
{/* BrowserRouter 包裹整个应用,为应用提供路由能力 */}
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
三、使用指南
- ReactDOM.createRoot :这是 React 18 引入的新 API,替代了旧版的
ReactDOM.render,它开启了 React 的并发渲染能力,使应用在处理复杂交互时更加流畅。 <React.StrictMode>:相当于一个"质检员",在开发阶段帮你检查组件的副作用是否纯粹,发布到生产环境时会自动失效,不影响性能。
四、同步渲染与异步渲染
把 React 渲染一个页面的过程,比作"盖房子"。整个过程分为两个大阶段:
- Render 阶段(设计阶段):React 在内存里计算差异,画好图纸。这个阶段不碰真实的 DOM,所以可以暂停。
- Commit 阶段(施工阶段):React 拿着图纸,真正去修改网页(真实 DOM)。这个阶段必须一气呵成,不能中断,否则网页会显示一半的半成品,造成错乱。
1. 同步渲染流程(React 18 以前的做法)
通俗比喻:"一根筋的施工队"。一旦开工,从设计到施工,必须一口气干完,哪怕外面天塌了(用户疯狂点击页面),施工队也不理你,直到房子盖完。
具体流程:
- 触发更新 :比如你调用了
setState。 - 一口气算到底(Render):React 开始遍历组件树,计算差异。这个过程是不可中断的,它会一直占用浏览器的 JavaScript 主线程。
- 一口气改到底(Commit):计算完差异后,立刻把修改应用到真实 DOM 上,执行生命周期钩子。
- 释放主线程:全部干完后,浏览器才能喘口气,去响应用户的其他操作。
缺点:如果组件树非常庞大,计算时间太长,页面就会"卡死"(冻结),用户点什么都没反应。
2. 并发渲染流程(React 18 的新做法)
通俗比喻:"懂得时间管理的大师"。大师在盖房子时,会设定一个"时间片"(比如 5 毫秒)。干一会儿活,就抬头看看有没有更紧急的事情(比如用户点击)。如果有,就放下手里的砖头,先去处理紧急事件,处理完再回来接着盖。
具体流程:
- 触发更新并分配优先级:React 会给更新打上"优先级标签"(Lane 模型)。用户的点击是"高优先级",后台的数据加载是"低优先级"。
- 可中断的计算(Render) :React 开始计算差异。但每处理完一个节点(Fiber 节点),它就会问自己一句:"时间片用完了吗?有更高优先级的任务吗?"
- 如果时间没用完,继续算下一个节点。
- 如果时间用完了,或者来了个高优先级任务,React 就会暂停(中断)当前的计算,把控制权还给浏览器去响应用户,等空闲了再恢复接着算。
- 无缝的施工(Commit):当 Render 阶段终于算完了,进入 Commit 阶段。这时候依然是不可中断的,必须一口气把 DOM 改完,保证用户看到的是完整的界面。