从 Vite 迁移
本指南将帮助你将现有的 Vite 应用迁移到 Next.js。
为什么要切换?
有几个原因可能会让你想从 Vite 切换到 Next.js:
初始页面加载时间慢
如果你使用 React 的默认 Vite 插件 构建应用,你的应用是一个纯客户端应用。客户端应用 (也称为单页应用,SPAs) 通常会遇到初始页面加载时间慢的问题。这是由于以下几个原因:
- 浏览器需要等待 React 代码和整个应用bundle下载并运行,然后你的代码才能发送请求加载数据。
- 随着每个新功能和额外依赖的添加,你的应用代码会不断增长。
没有自动代码分割
前面提到的加载时间慢的问题可以通过代码分割来一定程度上管理。然而,如果你尝试手动进行代码分割,通常会让性能变得更糟。手动代码分割很容易无意中引入网络瀑布流。Next.js 的路由器内置了自动代码分割功能。
网络瀑布流
应用性能不佳的一个常见原因是在获取数据时进行顺序性的客户端-服务器请求。SPA 中常见的数据获取模式是最初渲染一个占位符,然后在组件挂载后获取数据。不幸的是,这意味着子组件在父组件完成加载自身数据之前无法开始获取数据。
虽然 Next.js 支持在客户端获取数据,但它也给你选择将数据获取转移到服务器,这可以消除客户端-服务器瀑布流。
快速且有意图的加载状态
通过内置对 React Suspense 的流式传输支持,你可以更有意图地决定 UI 的哪些部分要先加载以及加载顺序,而不会引入网络瀑布流。
这使你能够构建加载更快的页面,并消除 布局偏移。
选择数据获取策略
根据你的需求,Next.js 允许你在页面和组件级别选择数据获取策略。你可以决定在构建时、服务器请求时或客户端获取数据。例如,你可以在构建时从 CMS 获取数据并渲染你的博客文章,然后可以在 CDN 上高效缓存。
中间件
Next.js 中间件 允许你在请求完成之前在服务器上运行代码。这对于避免在用户访问只有认证用户才能访问的页面时出现未认证内容的闪现特别有用,可以将用户重定向到登录页面。中间件也适用于实验和 国际化。
内置优化
图片、字体 和 第三方脚本 通常对应用的性能有重大影响。Next.js 带有内置组件,可以自动为你优化这些内容。
迁移步骤
我们的迁移目标是尽快获得一个可工作的 Next.js 应用,这样你就可以逐步采用 Next.js 的功能。首先,我们将保持它作为一个纯客户端应用 (SPA),不迁移你现有的路由器。这有助于最小化迁移过程中遇到问题的机会,并减少合并冲突。
步骤 1:安装 Next.js 依赖
你需要做的第一件事是安装 next
作为依赖:
步骤 2:创建 Next.js 配置文件
在项目根目录创建一个 next.config.mjs
文件。这个文件将保存你的 Next.js 配置选项。
提示: Next.js 配置文件可以使用
.js
或.mjs
扩展名。
步骤 3:更新 TypeScript 配置
如果你使用 TypeScript,你需要更新 tsconfig.json
文件,使其与 Next.js 兼容。如果你不使用 TypeScript,可以跳过这一步。
- 删除对
tsconfig.node.json
的 项目引用 - 在
include
数组 中添加./dist/types/**/*.ts
和./next-env.d.ts
- 在
exclude
数组 中添加./node_modules
- 在
compilerOptions
的plugins
数组 中添加{ "name": "next" }
:"plugins": [{ "name": "next" }]
- 将
esModuleInterop
设置为true
:"esModuleInterop": true
- 将
jsx
设置为preserve
:"jsx": "preserve"
- 将
allowJs
设置为true
:"allowJs": true
- 将
forceConsistentCasingInFileNames
设置为true
:"forceConsistentCasingInFileNames": true
- 将
incremental
设置为true
:"incremental": true
以下是一个包含这些更改的 tsconfig.json
示例:
你可以在 Next.js 文档 中找到更多关于配置 TypeScript 的信息。
步骤 4:创建根布局
Next.js App Router 应用必须包含一个 根布局 文件,这是一个 React 服务器组件,它将包裹你应用中的所有页面。这个文件定义在 app
目录的顶层。
Vite 应用中最接近根布局文件的等价物是 index.html
文件,它包含你的 <html>
、<head>
和 <body>
标签。
在这一步中,你将把 index.html
文件转换为根布局文件:
- 在
src
目录中创建一个新的app
目录。 - 在该
app
目录内创建一个新的layout.tsx
文件:
提示:布局文件可以使用
.js
、.jsx
或.tsx
扩展名。
- 将
index.html
文件的内容复制到之前创建的<RootLayout>
组件中,同时将body.div#root
和body.script
标签替换为<div id="root">{children}</div>
:
- Next.js 已经默认包含了 meta charset 和 meta viewport 标签,所以你可以安全地从
<head>
中删除这些标签:
- 任何 元数据文件,如
favicon.ico
、icon.png
、robots.txt
只要放置在app
目录的顶层,就会自动添加到应用的<head>
标签中。将 所有支持的文件 移动到app
目录后,你可以安全地删除它们的<link>
标签:
- 最后,Next.js 可以使用 元数据 API 管理你最后的
<head>
标签。将你最后的元数据信息移动到一个导出的metadata
对象 中:
通过上述更改,你从在 index.html
中声明所有内容转变为使用 Next.js 内置的基于约定的方法 ( 元数据 API )。这种方法使你能够更轻松地改善页面的 SEO 和网络分享能力。
步骤 5:创建入口点页面
在 Next.js 中,你通过创建 page.tsx
文件来声明应用的入口点。Vite 中最接近这个文件的等价物是你的 main.tsx
文件。在这一步中,你将设置应用的入口点。
- 在你的
app
目录中创建一个[[...slug]]
目录。
由于在本指南中我们首先旨在将 Next.js 设置为 SPA (单页应用),你需要你的页面入口点能捕获应用的所有可能路由。为此,在你的 app
目录中创建一个新的 [[...slug]]
目录。
这个目录被称为 可选的全捕获路由段。Next.js 使用基于文件系统的路由器,其中 目录用于定义路由。这个特殊的目录将确保你应用的所有路由都被定向到其包含的 page.tsx
文件。
- 在
app/[[...slug]]
目录中创建一个新的page.tsx
文件,内容如下:
提示:页面文件可以使用
.js
、.jsx
或.tsx
扩展名。
这个文件是一个 服务器组件。当你运行 next build
时,该文件会被预渲染成一个静态资源。它不需要任何动态代码。
这个文件导入了我们的全局 CSS,并告诉 generateStaticParams
我们只会生成一个路由,即位于 /
的索引路由。
现在,让我们移动我们 Vite 应用的其余部分,这部分将只在客户端运行。
这个文件是一个 客户端组件,由 'use client'
指令定义。客户端组件仍然会在服务器上 预渲染为 HTML,然后再发送到客户端。
由于我们想要一个仅客户端的应用程序开始,我们可以配置 Next.js 以禁用从 App
组件向下的预渲染。
现在,更新你的入口点页面以使用新的组件:
步骤 6:更新静态图片导入
Next.js 处理静态图片导入的方式与 Vite 略有不同。在 Vite 中,导入图片文件会返回其公共 URL 作为字符串:
而在 Next.js 中,静态图片导入会返回一个对象。然后可以直接将该对象用于 Next.js 的 <Image>
组件,或者你可以使用对象的 src
属性与现有的 <img>
标签。
<Image>
组件具有 自动图片优化 的额外好处。<Image>
组件会根据图片的尺寸自动设置生成的 <img>
的 width
和 height
属性。这可以防止图片加载时的布局偏移。然而,如果你的应用包含只对其中一个维度进行样式设置而另一个维度没有设置为 auto
的图片,这可能会导致问题。当没有设置为 auto
时,该维度将默认为 <img>
维度属性的值,这可能导致图片显示失真。
保留 <img>
标签将减少应用中的更改量,并防止上述问题。之后你可以选择性地迁移到 <Image>
组件,以通过 配置加载器 来利用图片优化,或移动到默认的 Next.js 服务器,该服务器具有自动图片优化功能。
- 将从
/public
导入图片的绝对导入路径转换为相对导入:
- 将图片的
src
属性而不是整个图片对象传递给你的<img>
标签:
或者,你可以根据文件名引用图片资源的公共 URL。例如,public/logo.png
将为你的应用提供位于 /logo.png
的图片,这将是 src
的值。
警告: 如果你使用 TypeScript,在访问
src
属性时可能会遇到类型错误。现在你可以安全地忽略这些错误。它们将在本指南结束时得到修复。
步骤 7:迁移环境变量
Next.js 支持类似于 Vite 的 .env
环境变量。主要区别在于用于在客户端暴露环境变量的前缀。
- 将所有带有
VITE_
前缀的环境变量更改为NEXT_PUBLIC_
。
Vite 在特殊的 import.meta.env
对象上暴露了一些内置的环境变量,这些变量在 Next.js 中不受支持。你需要按如下方式更新它们的用法:
import.meta.env.MODE
⇒process.env.NODE_ENV
import.meta.env.PROD
⇒process.env.NODE_ENV === 'production'
import.meta.env.DEV
⇒process.env.NODE_ENV !== 'production'
import.meta.env.SSR
⇒typeof window !== 'undefined'
Next.js 也不提供内置的 BASE_URL
环境变量。但是,如果你需要,你仍然可以配置一个:
- 在你的
.env
文件中添加以下内容:
- 在你的
next.config.mjs
文件中将basePath
设置为process.env.NEXT_PUBLIC_BASE_PATH
:
- 将
import.meta.env.BASE_URL
的使用更新为process.env.NEXT_PUBLIC_BASE_PATH
步骤 8:更新 package.json
中的脚本
现在你应该能够运行你的应用来测试是否成功迁移到 Next.js。但在此之前,你需要用 Next.js 相关的命令更新 package.json
中的 scripts
,并将 .next
和 next-env.d.ts
添加到你的 .gitignore
中:
现在运行 npm run dev
,并打开 http://localhost:3000
。你应该能看到你的应用现在运行在 Next.js 上了。
示例: 查看 这个 pull request 以获取一个从 Vite 迁移到 Next.js 的工作示例。
步骤 9:清理
现在你可以清理你的代码库中与 Vite 相关的内容:
- 删除
main.tsx
- 删除
index.html
- 删除
vite-env.d.ts
- 删除
tsconfig.node.json
- 删除
vite.config.ts
- 卸载 Vite 依赖
下一步
如果一切按计划进行,你现在应该有一个作为单页应用运行的功能性 Next.js 应用。然而,你还没有利用 Next.js 的大部分优势,但你现在可以开始进行增量更改以获得所有好处。以下是你接下来可能想要做的事情:
- 从 React Router 迁移到 Next.js App Router 以获得:
- 自动代码分割
- 流式服务器渲染
- React 服务器组件
- 使用
<Image>
组件优化图片 - 使用
next/font
优化字体 - 使用
<Script>
组件优化第三方脚本 - 更新你的 ESLint 配置以支持 Next.js 规则