如果你在 2018 年前后学 React,第一课多半是 npx create-react-app my-app。它曾经是搭建 React 项目的标准答案。但如今 React 官方已经弃用它,转而推荐框架或 Vite。这中间发生了什么?本文讲清 CRA 是什么、为什么被放弃、为什么它难以自己配置 webpack,以及官方推荐的方向和 Vite 的不同。
一、create-react-app 是什么
CRA 由 Facebook(现 Meta)在 2016 年推出,目标是「零配置上手 React」。它由两部分组成:
create-react-app:脚手架 CLI,负责生成项目模板。react-scripts:项目依赖,封装了整套构建工具链——webpack、Babel、ESLint、Jest、PostCSS、webpack-dev-server 等。
使用方式极其简单:
npx create-react-app my-app
cd my-app
npm start
你不用写一行 webpack 配置,就能得到编译、热更新、打包、测试、代码检查等能力。在「配置地狱」的年代,这是巨大的进步,也正是它流行的原因。
但它的设计里埋着一个根本矛盾:为了「零配置」,它把配置完全封死了。
二、为什么不再推荐用 CRA
1. 官方已经弃用
2025 年 2 月,React 团队正式将 CRA 标记为弃用(deprecated),react-scripts 不再积极维护。React 官方文档也早已把新项目指引改为「推荐使用框架,或在需要纯 SPA 时使用 Vite」。也就是说,CRA 不再是被推荐的起点。
2. 构建慢,且随项目变大越来越慢
CRA 基于 webpack,而 webpack 的开发模式是先打包再启动:
- 项目越大,冷启动越慢,改一行代码的反馈也越慢。
- 热更新(HMR)在大项目里会出现明显延迟。
- 默认配置没有充分利用文件系统缓存、增量编译等现代优化。
在 Vite、Turbopack 等新工具把开发启动压到毫秒级的今天,CRA 的开发体验差距非常明显。
3. 技术栈陈旧
CRA 把 Babel、webpack、Jest 等一整套工具锁在 react-scripts 内部,版本更新滞后,用户无法选择更快的方案(如 SWC、esbuild)。想用新特性,只能等 CRA 升级——而它已经停止升级了。
4. 配置封闭,定制困难
这是最要命的一点,下一节单独展开。
5. 默认配置不再是最优
它默认引入较多 polyfill、不做极致的 Tree Shaking 和分包策略调整。在追求体积和性能的今天,这套默认值已经不够用。
三、为什么用 CRA 就「难以自己配置 webpack」
CRA 的核心卖点是「零配置」,代价就是你失去对构建的控制权。想改 webpack 只有三条路,且一条比一条难走:
1. 配置被藏起来了
webpack.config.js 不在项目里,而在 node_modules/react-scripts/config/ 下。你不改它,就只能用默认值;改了 node_modules,下次 npm install 就被覆盖。
2. eject 不可逆
npm run eject 会把配置释放到项目根目录,之后就能随意改。但它只能做一次、无法撤销:
- 从此构建配置由你自己维护,升级要靠手动合并。
- CRA 提供的封装红利(统一升级、一致性)全部消失。
- 项目里会突然多出一大堆你没写过、也不完全理解的配置。
大多数团队并不想为此买单。
3. 覆写工具很脆弱
为绕开 eject,社区出现了 craco、react-app-rewired 等方案:它们读取 CRA 的原始配置再做修改。
// craco.config.js
module.exports = {
webpack: { alias: { '@': path.resolve(__dirname, 'src') } },
};
问题在于,这些工具依赖 CRA 内部配置的具体结构。CRA(或它依赖的 webpack)一有变动,覆写就可能失效;而 CRA 本身又不再更新,等于把项目架在一个正在腐烂的地基上。
4. 想要深度优化时无从下手
现代构建常见的需求——精细的 splitChunks、runtimeChunk 缓存优化、Tree Shaking 调优、替换 Babel 为 SWC、自定义 loader——在 CRA 里要么做不了,要么得靠 hack。「零配置」在简单项目里是优点,在需要优化的项目里就成了枷锁。
四、官方推荐用什么替代 CRA
需要澄清一点:官方并没有「改用 Vite」作为唯一答案,而是给出了两条线:
- 全栈/生产级应用 → 用 React 框架,如 Next.js、Remix、Expo 等。
- 纯客户端 SPA / 想自己掌控 → 用 Vite(或 React Router 等)。
背后的判断是:
- 构建工具应该交给专业的工具或框架,React 专注做好 UI 层。
- CRA 试图「什么都管一点」,结果既不够轻(不如 Vite 快),也不够全(没有 SSR、路由、数据获取等框架能力)。
- Vite 基于 esbuild + Rollup,开发体验和构建质量都更符合当前标准。
所以更准确的说法是:官方不再推荐 CRA,把起点让给了「框架」和「Vite」——其中 Vite 对应「纯 React 应用」的场景。
五、Vite 和 CRA 有什么不同
| 维度 | CRA | Vite |
|---|---|---|
| 开发时构建 | webpack 先打包再启动 | 原生 ESM,不打包,esbuild 预构建依赖 |
| 启动/HMR | 慢,随项目增大而恶化 | 毫秒级,和项目规模基本无关 |
| 生产构建 | webpack | Rollup |
| 配置 | 隐藏,需 eject | 显式 vite.config.js,直接可改 |
| 扩展方式 | craco / eject(脆弱/不可逆) | 插件体系,Rollup 插件大多可用 |
| 环境变量 | REACT_APP_* + process.env | VITE_* + import.meta.env |
| HTML 入口 | public/index.html | 根目录 index.html |
| 代理 | src/setupProxy.js | server.proxy |
| 单元测试 | 内置 Jest | Vitest(需另装) |
| 转译器 | Babel | esbuild / SWC(可换) |
| 维护状态 | 已弃用 | 活跃维护 |
最关键的两点差异:
1. 开发范式不同。 CRA 的开发服务器需要先把整个应用打包好;Vite 利用浏览器原生 ESM,按需提供模块,因此启动几乎瞬时,HMR 也极快。
2. 配置哲学不同。 CRA 是「我帮你配好,你别动」;Vite 是「给你一份干净的配置,你来管」。前者在简单场景省事,后者在需要定制时省心。
六、从 CRA 迁移到 Vite 的要点
迁移通常并不复杂:
- 装
vite、@vitejs/plugin-react,加vite.config.js。 index.html放到根目录,脚本引用改为<script type="module" src="/src/main.jsx">。- 环境变量从
REACT_APP_*/process.env改为VITE_*/import.meta.env。 - 路径别名在
vite.config.js配resolve.alias,并同步tsconfig.json的paths。 - 代理从
setupProxy.js迁到server.proxy。 - SVG 组件、Sass 等能力改用对应 Vite 插件。
React.lazy、动态 import()、CSS Modules 等用法基本无缝,改动集中在入口和配置层面。
小结
| 问题 | 结论 |
|---|---|
| CRA 是什么 | 封装了 webpack 等工具链的 React 脚手架 |
| 为什么弃用 | 构建慢、技术老旧、配置封闭、官方已停止维护 |
| 为什么难改配置 | 配置被隐藏,eject 不可逆,覆写工具脆弱 |
| 官方推荐什么 | 框架(Next.js 等)或 Vite |
| 和 Vite 的区别 | 开发不打包、启动快、配置显式可改 |
一句话:CRA 用「隐藏配置」换来了当年的上手便利,却在需要性能和定制时变成了束缚;Vite 则用「显式配置 + 原生 ESM」同时给了速度和自由——这正是它取代 CRA 成为 React 纯应用起点的原因。