废弃Create-React-App
# Create React App 的日落
2025年2月14日,作者:Matt Carroll 和 Ricky Hanlon
今天,我们宣布将 Create React App 弃用于新应用,并鼓励现有应用迁移到框架,或迁移到构建工具如 Vite、Parcel 或 RSBuild。
我们还提供了文档,适用于以下情况:框架不适合你的项目、你想构建自己的框架,或者你只是想通过从头构建 React 应用来学习 React 的工作原理。
# 背景
当我们于 2016 年发布 Create React App 时,还没有明确的方法来构建一个新的 React 应用。
要创建一个 React 应用,你必须安装一堆工具并将它们组合在一起,以支持基本功能,如 JSX、代码检查和热重载。这非常难以正确完成,因此社区为常见设置创建了样板文件。然而,样板文件难以更新,碎片化使得 React 难以发布新功能。
Create React App 通过将多个工具组合成一个推荐的配置来解决这些问题。这使得应用能够以简单的方式升级到新的工具功能,并允许 React 团队将非平凡的工具更改(如快速刷新支持、React Hooks 的代码检查规则)部署到尽可能广泛的受众。
这种模式变得如此流行,以至于今天有一整类工具以这种方式工作。
# 弃用 Create React App
尽管 Create React App 使得入门变得容易,但它有几个限制,使得构建高性能的生产应用变得困难。原则上,我们可以通过将其演变成一个框架来解决这些问题。
然而,由于 Create React App 目前没有活跃的维护者,并且已经有许多现有的框架解决了这些问题,我们决定弃用 Create React App。
从今天开始,如果你安装一个新应用,你将看到一个弃用警告:
create-react-app 已弃用。
你可以在 react.dev 上找到最新的 React 框架列表。
更多信息请参见:react.dev/link/cra
此错误消息每次安装只会显示一次。
2
3
4
5
6
我们还在 Create React App 网站和 GitHub 仓库中添加了弃用通知。Create React App 将继续以维护模式工作,并且我们已经发布了新版本的 Create React App 以支持 React 19。
# 如何迁移到框架
我们建议使用框架创建新的 React 应用。我们推荐的所有框架都支持客户端渲染(CSR)和单页应用(SPA),并且可以部署到 CDN 或静态托管服务,而无需服务器。
对于现有应用,这些指南将帮助你迁移到仅客户端的 SPA:
- Next.js 的 Create React App 迁移指南 (opens new window)
- React Router 的框架采用指南 (opens new window)
- Expo webpack 到 Expo Router 迁移指南 (opens new window)
# 如何迁移到构建工具
如果你的应用有特殊的限制,或者你更喜欢通过构建自己的框架来解决这些问题,或者你只是想从头学习 React 的工作原理,你可以使用 Vite、Parcel 或 Rsbuild 来构建自定义的 React 设置。
对于现有应用,这些指南将帮助你迁移到构建工具:
- Vite 的 Create React App 迁移指南 (opens new window)
- Parcel 的 Create React App 迁移指南 (opens new window)
- Rsbuild 的 Create React App 迁移指南 (opens new window)
为了帮助你开始使用 Vite、Parcel 或 Rsbuild,我们添加了新的文档:从头构建 React 应用 (opens new window)。
# 深入探讨
# 我需要框架吗?
继续阅读以了解更多关于构建工具的局限性以及我们为什么推荐框架。
# 构建工具的局限性
Create React App 和类似的构建工具使得开始构建 React 应用变得容易。在运行 npx create-react-app my-app
后,你将获得一个完全配置好的 React 应用,带有开发服务器、代码检查和生产构建。
例如,如果你正在构建一个内部管理工具,你可以从一个登录页面开始:
export default function App() {
return (
<div>
<h1>欢迎使用管理工具!</h1>
</div>
)
}
2
3
4
5
6
7
这允许你立即开始使用 React 进行编码,并享受 JSX、默认代码检查规则和用于开发和生产的打包器等特性。然而,这个设置缺少构建真实生产应用所需的工具。
大多数生产应用需要解决诸如路由、数据获取和代码拆分等问题。
# 路由
Create React App 不包含特定的路由解决方案。如果你刚刚开始,一个选择是使用 useState
来切换路由。但这样做意味着你无法分享应用的链接——每个链接都会指向同一个页面——并且随着时间的推移,应用的结构会变得困难:
import {useState} from 'react';
import Home from './Home';
import Dashboard from './Dashboard';
export default function App() {
// ❌ 在状态中路由不会创建 URL
const [route, setRoute] = useState('home');
return (
<div>
{route === 'home' && <Home />}
{route === 'dashboard' && <Dashboard />}
</div>
)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这就是为什么大多数使用 Create React App 的应用会通过添加路由库(如 React Router 或 Tanstack Router)来解决路由问题。通过路由库,你可以为应用添加额外的路由,这为应用的结构提供了意见,并允许你开始分享路由链接。例如,使用 React Router,你可以定义路由:
import {RouterProvider, createBrowserRouter} from 'react-router';
import Home from './Home';
import Dashboard from './Dashboard';
// ✅ 每个路由都有自己的 URL
const router = createBrowserRouter([
{path: '/', element: <Home />},
{path: '/dashboard', element: <Dashboard />}
]);
export default function App() {
return (
<RouterProvider value={router} />
)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
通过这个更改,你可以分享一个指向 /dashboard
的链接,应用将导航到仪表板页面。一旦你有了路由库,你可以添加额外的功能,如嵌套路由、路由守卫和路由过渡,这些功能在没有路由库的情况下很难实现。
这里有一个权衡:路由库增加了应用的复杂性,但它也增加了难以实现的功能。
# 数据获取
Create React App 中的另一个常见问题是数据获取。Create React App 不包含特定的数据获取解决方案。如果你刚刚开始,一个常见的选择是在 useEffect
中使用 fetch
来加载数据。
但这样做意味着数据在组件渲染后才会获取,这可能导致网络瀑布流。网络瀑布流是由于在应用渲染时获取数据,而不是在代码下载时并行获取数据:
export default function Dashboard() {
const [data, setData] = useState(null);
// ❌ 在组件中获取数据会导致网络瀑布流
useEffect(() => {
fetch('/api/data')
.then(response => response.json())
.then(data => setData(data));
}, []);
return (
<div>
{data.map(item => <div key={item.id}>{item.name}</div>)}
</div>
)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在 useEffect
中获取数据意味着用户需要等待更长时间才能看到内容,即使数据可能已经提前获取。为了解决这个问题,你可以使用数据获取库,如 React Query、SWR、Apollo 或 Relay,它们提供了预取数据的选项,以便在组件渲染之前启动请求。
这些库在与路由的“加载器”模式集成时效果最佳,以在路由级别指定数据依赖关系,从而允许路由器优化你的数据获取:
export async function loader() {
const response = await fetch(`/api/data`);
const data = await response.json();
return data;
}
// ✅ 在代码下载时并行获取数据
export default function Dashboard({loaderData}) {
return (
<div>
{loaderData.map(item => <div key={item.id}>{item.name}</div>)}
</div>
)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
在初始加载时,路由器可以在路由渲染之前立即获取数据。当用户在应用中导航时,路由器能够同时获取数据和路由,从而并行化获取。这减少了用户看到屏幕上内容所需的时间,并可以改善用户体验。
然而,这需要正确配置应用中的加载器,并在复杂性和性能之间进行权衡。
# 代码拆分
Create React App 中的另一个常见问题是代码拆分。Create React App 不包含特定的代码拆分解决方案。如果你刚刚开始,你可能根本不会考虑代码拆分。
这意味着你的应用作为一个单一的包发布:
- bundle.js 75kb
但对于理想的性能,你应该将代码“拆分”为单独的包,以便用户只需下载他们需要的内容。这减少了用户加载应用所需的时间,因为他们只需下载他们正在查看的页面所需的代码。
- core.js 25kb
- home.js 25kb
- dashboard.js 25kb
2
3
一种进行代码拆分的方法是使用 React.lazy
。然而,这意味着代码在组件渲染之前不会被获取,这可能导致网络瀑布流。更优的解决方案是使用路由器的功能,在代码下载时并行获取代码。例如,React Router 提供了一个 lazy
选项,用于指定路由应进行代码拆分并优化其加载时间:
import Home from './Home';
import Dashboard from './Dashboard';
// ✅ 路由在渲染之前下载
const router = createBrowserRouter([
{path: '/', lazy: () => import('./Home')},
{path: '/dashboard', lazy: () => import('Dashboard')}
]);
2
3
4
5
6
7
8
优化的代码拆分很难正确实现,并且很容易犯错误,导致用户下载比他们需要的更多的代码。它与路由和数据加载解决方案集成时效果最佳,以最大化缓存、并行化获取并支持“交互时导入”模式。
# 更多...
这些只是 Create React App 局限性的一些例子。
一旦你集成了路由、数据获取和代码拆分,你现在还需要考虑挂起状态、导航中断、向用户显示错误消息以及数据的重新验证。用户需要解决的整个类别的问题包括:
- 可访问性
- 资源加载
- 身份验证
- 缓存
- 错误处理
- 数据突变
- 导航
- 乐观更新
- 渐进增强
- 服务器端渲染
- 静态站点生成
- 流式传输
所有这些共同作用,以创建最优的加载序列。
在 Create React App 中单独解决这些问题可能很困难,因为每个问题都与其他问题相互关联,并且可能需要用户不熟悉的领域的深厚专业知识。为了解决这些问题,用户最终会在 Create React App 之上构建自己的定制解决方案,而这正是 Create React App 最初试图解决的问题。
# 为什么我们推荐框架
尽管你可以在 Create React App、Vite 或 Parcel 等构建工具中自己解决所有这些部分,但很难做好。就像 Create React App 本身集成了多个构建工具一样,你需要一个工具来集成所有这些功能,以提供最佳的用户体验。
这类集成了构建工具、渲染、路由、数据获取和代码拆分的工具被称为“框架”——或者如果你更喜欢将 React 本身称为框架,你可能会称它们为“元框架”。
框架对你的应用结构施加了一些意见,以提供更好的用户体验,就像构建工具对工具施加了一些意见以使其更容易使用一样。这就是为什么我们开始推荐 Next.js、React Router 和 Expo 等框架用于新项目。
框架提供了与 Create React App 相同的入门体验,但也提供了解决用户在真实生产应用中需要解决的问题的方案。
# 深入探讨
# 服务器渲染是可选的
注意:服务器渲染不仅仅是为了 SEO 一个常见的误解是服务器渲染仅用于 SEO。
虽然服务器渲染可以改善 SEO,但它也通过减少用户在屏幕上看到内容之前需要下载和解析的 JavaScript 数量来提高性能。
这就是为什么 Chrome 团队鼓励开发者考虑静态或服务器端渲染,而不是完全客户端渲染,以实现最佳性能。
# 致谢
感谢 Dan Abramov 创建了 Create React App,以及 Joe Haddad、Ian Schmitz、Brody McKee 和许多其他人在多年来维护 Create React App。感谢 Brooks Lybrand、Dan Abramov、Devon Govett、Eli White、Jack Herrington、Joe Savona、Lauren Tan、Lee Robinson、Mark Erikson、Ryan Florence、Sophie Alpert、Tanner Linsley 和 Theo Browne 审阅并提供对此文章的反馈。