从 Create React App 迁移
本指南将帮助你将现有的 Create React App (CRA) 站点迁移到 Next.js。
为什么要切换?
你可能想要从 Create React App 切换到 Next.js 的原因有几个:
初始页面加载时间慢
Create React App 纯粹使用客户端 React。纯客户端应用程序,也称为单页应用程序 (SPA),通常会遇到初始页面加载时间慢的问题。这发生的原因有几个:
- 浏览器需要等待 React 代码和你的整个应用程序包下载并运行,然后你的代码才能发送请求来加载数据。
- 你的应用程序代码会随着你添加的每个新功能和依赖项而增长。
没有自动代码拆分
先前加载时间慢的问题可以通过代码拆分在某种程度上得到缓解。但是,如果你尝试手动进行代码拆分,你可能会无意中引入网络瀑布流。Next.js 提供了内置在其路由器和构建管道中的自动代码拆分和 tree-shaking。
网络瀑布流
当应用程序进行连续的客户端-服务器请求以获取数据时,就会发生常见的性能问题。在 SPA 中,数据获取的一种模式是渲染占位符,然后在组件挂载后获取数据。不幸的是,子组件只能在其父组件完成加载自身数据后才能开始获取数据,从而导致请求的“瀑布流”。
虽然 Next.js 支持客户端数据获取,但 Next.js 也允许你将数据获取移动到服务器。这通常可以完全消除客户端-服务器瀑布流。
快速且有意的加载状态
凭借对通过 React Suspense 进行流式处理的内置支持,你可以定义 UI 的哪些部分首先加载以及以什么顺序加载,而不会创建网络瀑布流。
这使你能够构建加载速度更快的页面,并消除布局偏移。
选择数据获取策略
根据你的需求,Next.js 允许你在页面或组件级别上选择数据获取策略。例如,你可以从你的 CMS 获取数据并在构建时渲染博客文章 (SSG) 以获得快速加载速度,或者在必要时在请求时获取数据 (SSR)。
中间件
Next.js 中间件允许你在服务器上在请求完成之前运行代码。例如,你可以通过在中间件中将用户重定向到仅限身份验证页面的登录页面,来避免未经身份验证内容的闪烁。你还可以将其用于 A/B 测试、实验和国际化等功能。
内置优化
图像、字体和第三方脚本通常对应用程序的性能有很大影响。Next.js 包含专门的组件和 API,可以自动为你优化它们。
迁移步骤
我们的目标是尽快获得一个可用的 Next.js 应用程序,以便你可以逐步采用 Next.js 功能。首先,我们将你的应用程序视为纯客户端应用程序 (SPA),而不会立即替换你现有的路由器。这降低了复杂性和合并冲突。
注意:如果你正在使用高级 CRA 配置,例如
package.json中的自定义homepage字段、自定义 service worker 或特定的 Babel/webpack 调整,请参阅本指南末尾的其他注意事项部分,以获取有关在 Next.js 中复制或调整这些功能的提示。
步骤 1:安装 Next.js 依赖项
在现有项目中安装 Next.js
npm install next@latest步骤 2:创建 Next.js 配置文件
在项目根目录(与 package.json 同级)创建一个 next.config.ts。此文件保存你的Next.js 配置选项。
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
output: 'export', // Outputs a Single-Page Application (SPA)
distDir: 'build', // Changes the build output directory to `build`
}
export default nextConfig注意:使用
output: 'export'表示你正在进行静态导出。你将无法访问服务器端功能,如 SSR 或 API。你可以删除此行以利用 Next.js 服务器功能。
步骤 3:创建根布局
Next.js App Router 应用程序必须包含一个根布局文件,这是一个React Server Component,它将包裹你的所有页面。
CRA 应用程序中根布局文件最接近的等效项是 public/index.html,其中包含你的 <html>、<head> 和 <body> 标签。
- 在你的
src目录中(或者如果你更喜欢根目录中的app,则在项目根目录中)创建一个新的app目录。 - 在
app目录中,创建一个layout.tsx(或layout.js)文件
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return '...'
}现在将旧的 index.html 的内容复制到此 <RootLayout> 组件中。将 body div#root(和 body noscript)替换为 <div id="root">{children}</div>。
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<meta charSet="UTF-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}须知:默认情况下,Next.js 会忽略 CRA 的
public/manifest.json、其他图标和测试配置。如果你需要这些,Next.js 通过其Metadata API 和 Testing 设置提供支持。
步骤 4:元数据
Next.js 自动包含 <meta charset="UTF-8" /> 和 <meta name="viewport" content="width=device-width, initial-scale=1" /> 标签,因此你可以从 <head> 中删除它们
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}任何元数据文件(例如 favicon.ico、icon.png、robots.txt)都会自动添加到应用程序 <head> 标签中,只要你将它们放置在 app 目录的顶层即可。将所有支持的文件移动到 app 目录后,你可以安全地删除它们的 <link> 标签
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}最后,Next.js 可以使用 Metadata API 管理你最后的 <head> 标签。将你最后的元数据信息移动到导出的 metadata 对象中
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'React App',
description: 'Web site created with Next.js.',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<div id="root">{children}</div>
</body>
</html>
)
}通过上述更改,你从在 index.html 中声明所有内容转变为使用 Next.js 内置于框架中的基于约定的方法 (Metadata API)。这种方法使你能够更轻松地改进页面的 SEO 和 Web 共享性。
步骤 5:样式
与 CRA 一样,Next.js 开箱即用地支持 CSS Modules。它还支持全局 CSS 导入。
如果你有全局 CSS 文件,请将其导入到你的 app/layout.tsx 中
import '../index.css'
export const metadata = {
title: 'React App',
description: 'Web site created with Next.js.',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<div id="root">{children}</div>
</body>
</html>
)
}如果你正在使用 Tailwind CSS,请参阅我们的安装文档。
步骤 6:创建入口点页面
Create React App 使用 src/index.tsx(或 index.js)作为入口点。在 Next.js (App Router) 中,app 目录中的每个文件夹都对应一个路由,并且每个文件夹都应该有一个 page.tsx。
由于我们现在希望将应用保持为 SPA 并拦截所有路由,因此我们将使用可选的 catch-all 路由。
- 在
app内创建一个[[...slug]]目录。
app
┣ [[...slug]]
┃ ┗ page.tsx
┣ layout.tsx- 将以下内容添加到
page.tsx:
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // We'll update this
}这告诉 Next.js 为空 slug (/) 生成单个路由,有效地将所有路由映射到同一页面。此页面是一个 Server Component,预渲染为静态 HTML。
步骤 7:添加仅客户端入口点
接下来,我们将你的 CRA 的根 App 组件嵌入到Client Component 中,以便所有逻辑都保留在客户端。如果这是你第一次使用 Next.js,值得了解的是,客户端组件(默认情况下)仍然在服务器上预渲染。你可以将它们视为具有运行客户端 JavaScript 的附加功能。
在 app/[[...slug]]/ 中创建一个 client.tsx(或 client.js)
'use client'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../App'), { ssr: false })
export function ClientOnly() {
return <App />
}'use client'指令使此文件成为 Client Component。- 带有
ssr: false的dynamic导入禁用<App />组件的服务器端渲染,使其成为真正的仅客户端(SPA)。
现在更新你的 page.tsx(或 page.js)以使用你的新组件
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}步骤 8:更新静态图像导入
在 CRA 中,导入图像文件会将其公共 URL 作为字符串返回
import image from './img.png'
export default function App() {
return <img src={image} />
}使用 Next.js,静态图像导入返回一个对象。然后可以将该对象直接与 Next.js <Image> 组件一起使用,或者你可以将该对象的 src 属性与你现有的 <img> 标签一起使用。
<Image> 组件还具有自动图像优化的额外好处。<Image> 组件会自动根据图像的尺寸设置生成的 <img> 的 width 和 height 属性。这可以防止图像加载时的布局偏移。但是,如果你的应用包含仅对其一个维度设置样式而另一个维度未设置为 auto 的图像,则可能会导致问题。当未设置为 auto 时,维度将默认为 <img> dimension 属性的值,这可能会导致图像看起来失真。
保留 <img> 标签将减少应用程序中的更改量,并防止上述问题。然后,你可以选择稍后迁移到 <Image> 组件,以通过配置加载器或迁移到具有自动图像优化的默认 Next.js 服务器来利用优化图像。
将从 /public 导入的图像的绝对导入路径转换为相对导入
// Before
import logo from '/logo.png'
// After
import logo from '../public/logo.png'将图像 src 属性而不是整个图像对象传递给你的 <img> 标签
// Before
<img src={logo} />
// After
<img src={logo.src} />或者,你可以根据文件名引用图像资产的公共 URL。例如,public/logo.png 将在 /logo.png 为你的应用程序提供图像,这将是 src 值。
警告: 如果您正在使用 TypeScript,当访问
src属性时,您可能会遇到类型错误。要修复这些错误,您需要将next-env.d.ts添加到您的tsconfig.json文件的include数组 中。当您在步骤 9 运行您的应用程序时,Next.js 将会自动生成此文件。
步骤 9:迁移环境变量
Next.js 支持 环境变量,方式类似于 CRA,但是要求任何您想要在浏览器中暴露的变量都必须带有 NEXT_PUBLIC_ 前缀。
主要的区别是用于在客户端暴露环境变量的前缀。将所有带有 REACT_APP_ 前缀的环境变量更改为 NEXT_PUBLIC_。
步骤 10:更新 package.json 中的脚本
更新您的 package.json 脚本以使用 Next.js 命令。另外,将 .next 和 next-env.d.ts 添加到您的 .gitignore 中
{
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "npx serve@latest ./build"
}
}# ...
.next
next-env.d.ts现在您可以运行
npm run dev打开 https://:3000。您现在应该看到您的应用程序在 Next.js 上运行(以 SPA 模式)。
步骤 11:清理
您现在可以移除特定于 Create React App 的工件
public/index.htmlsrc/index.tsxsrc/react-app-env.d.tsreportWebVitals设置react-scripts依赖项(从package.json中卸载它)
其他注意事项
在 CRA 中使用自定义 homepage
如果您在 CRA 的 package.json 中使用了 homepage 字段以在特定子路径下提供应用程序,您可以使用 next.config.ts 中的 basePath 配置 在 Next.js 中复制该行为
import { NextConfig } from 'next'
const nextConfig: NextConfig = {
basePath: '/my-subpath',
// ...
}
export default nextConfig处理自定义 Service Worker
如果您使用了 CRA 的 service worker(例如,来自 create-react-app 的 serviceWorker.js),您可以学习如何使用 Next.js 创建 渐进式 Web 应用程序 (PWA)。
代理 API 请求
如果您的 CRA 应用程序在 package.json 中使用 proxy 字段将请求转发到后端服务器,您可以使用 next.config.ts 中的 Next.js 重写 来复制此行为
import { NextConfig } from 'next'
const nextConfig: NextConfig = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://your-backend.com/:path*',
},
]
},
}自定义 Webpack / Babel 配置
如果您在 CRA 中有自定义 webpack 或 Babel 配置,您可以在 next.config.ts 中扩展 Next.js 的配置
import { NextConfig } from 'next'
const nextConfig: NextConfig = {
webpack: (config, { isServer }) => {
// Modify the webpack config here
return config
},
}
export default nextConfig注意:这将需要通过从您的
dev脚本中移除--turbopack来禁用 Turbopack。
TypeScript 设置
如果您有 tsconfig.json,Next.js 会自动设置 TypeScript。确保 next-env.d.ts 列在您的 tsconfig.json 的 include 数组中
{
"include": ["next-env.d.ts", "app/**/*", "src/**/*"]
}Bundler 兼容性
Create React App 和 Next.js 默认都使用 webpack 进行打包。Next.js 还提供了 Turbopack,以便在本地开发中获得更快的速度,使用
next dev --turbopack如果您需要迁移来自 CRA 的高级 webpack 设置,您仍然可以提供 自定义 webpack 配置。
下一步
如果一切正常,您现在应该有一个作为单页应用程序运行的 Next.js 应用程序。您尚未利用 Next.js 的功能,如服务器端渲染或基于文件的路由,但您现在可以逐步进行
- 将 React Router 迁移 到 Next.js App Router 以获得
- 自动代码拆分
- 流式服务器渲染
- React Server Components
- 使用
<Image>组件 优化图像 - 使用
next/font优化字体 - 使用
<Script>组件 优化第三方脚本 - 通过运行
npx next lint并配置使其匹配您的项目需求,启用 ESLint 并使用 Next.js 推荐的规则
注意:使用静态导出 (
output: 'export') 目前不支持useParams钩子或其他服务器功能。要使用所有 Next.js 功能,请从您的next.config.ts中移除output: 'export'。
这篇文章对您有帮助吗?