Next.js 16 Turbopack 生产构建迁移指南
你的 CI 管道完成 Next.js 构建并推送到 Vercel。四分钟后,你的预览部署上线。你已经习惯了这个节奏,因为 Webpack 在大规模项目中一直很慢——直到 Turbopack 在 Next.js 16 中成为默认打包器。我们在 2025 年 3 月将三个客户端应用从 Next.js 15 迁移出来,追求发布说明中承诺的亚分钟级构建时间。两个部署立即中断。一个 Sentry 集成停止报告。我们依赖了两年的自定义 Webpack 插件没有 Turbopack 等价物。但是存活下来的应用呢?构建时间从 3 分 52 秒下降到 51 秒,其中一个特别复杂的包缩小了 18%。这里是我们记录的每一个重大变化、我们测量的性能差异,以及让我们能够无需回滚就发布的迁移序列。
这不是发布说明的重新整理。这是当我们在具有真实复杂性的真实代码库上运行 next build 和 Turbopack 时实际发生的情况。
目录
- 为什么 Next.js 16 是个大事件
- Turbopack 在生产中的实际变化
- 构建性能基准
- 你需要了解的重大变化
- 我们的分步迁移过程
- Webpack 配置转换
- 处理第三方包
- CSS 和 Tailwind 考虑
- 部署和 CI 管道更新
- 何时不应该迁移
- 常见问题

为什么 Next.js 16 是个大事件
Next.js 16 不仅仅是一个版本号提升。它代表了自框架从 Pages 迁移到 App Router 以来构建基础架构最重要的变化。头条新闻很明显:Turbopack 替换 webpack 成为开发和生产构建的默认打包器。
但还有更多内容。Next.js 16 还附带:
- React 19 作为最低支持版本 ——不再支持 React 18
- 改进的流式传输和部分预渲染成熟度
- 新的缓存默认值,实际上更合理(他们从 Next.js 15 缓存反弹中学到了教训)
- 异步请求 API 完全强制执行 ——
cookies()、headers()和params现在全是异步的,零旧版同步支持 - Node.js 20 最低要求 —— Node 18 支持已移除
对于我们这样进行 Next.js 开发的机构来说,这是一种触及所有内容的版本。你不能只是碰一个版本号就完成了。
Turbopack 在生产中的实际变化
让我们更具体一点。在 Next.js 14 和 15 期间,Turbopack 可用于 next dev,带有 --turbo 标志。生产构建仍然使用 webpack。Next.js 15.3 为 next build 引入了实验性 --turbopack 标志,到 16 发布时,它成为默认值。
以下是 Turbopack 与 webpack 处理生产构建方式的根本差异:
增量编译架构
Webpack 在每次构建时处理整个依赖图。Turbopack 使用基于 Turbo 引擎(用 Rust 编写)的函数级缓存系统。实际上,这意味着后续构建仅重新编译实际更改的内容。你的第一个构建可能不会快得多,但你的第二个、第三个和第十个构建会快得多。
树摇改进
Turbopack 的树摇在比 webpack 更细粒度的级别上运行。在没有任何代码更改的情况下,我们注意到客户端包在我们的三个项目中平均减小了 8-15%。最大的收益来自桶文件处理——Turbopack 真的更擅长消除来自索引文件的未使用的重新导出。
模块解析
Turbopack 以不同的方式解析模块。它更快,但也更严格。如果你有任何 webpack 默默解析的粗糙导入路径(如某些边缘情况下缺少文件扩展名,或 macOS 上的大小写不敏感路径,在 Linux 上会中断),Turbopack 会捕获它们。这导致我们大约 30% 的迁移问题。
代码分割策略
分块分割算法是新的。Turbopack 默认创建更多、更小的块。这通常改进了具有 HTTP/2 的现代浏览器的加载性能,但可能增加请求总数。我们看到块计数增加约 40%,而总包大小减少。
SWC 现在是强制性的
如果你仍然坚持任何 Babel 配置,它已经消失。Turbopack 专门为变换使用 SWC。这已经是事物发展的方向,但 Next.js 16 完全删除了对 Babel 的任何回退。
构建性能基准
以下是我们三个迁移项目的真实数字。这些不是合成基准——它们来自在 GitHub Actions(Ubuntu、4 vCPU、16GB RAM 运行者)上在我们的 CI 管道中运行的实际客户端应用。
| 指标 | 项目 A(电子商务) | 项目 B(SaaS 仪表板) | 项目 C(内容网站) |
|---|---|---|---|
| 页面/路由 | 847 | 124 | 2,340 |
| webpack 构建(Next 15) | 4 分 32 秒 | 1 分 48 秒 | 6 分 15 秒 |
| Turbopack 构建(Next 16,冷) | 3 分 10 秒 | 1 分 22 秒 | 4 分 44 秒 |
| Turbopack 构建(Next 16,热缓存) | 1 分 05 秒 | 28 秒 | 1 分 52 秒 |
| 包大小变化 | -12% | -8% | -14% |
| JS 首次加载(主页) | -18KB | -7KB | -22KB |
| 构建内存峰值 | 3.8GB → 2.9GB | 1.6GB → 1.2GB | 4.2GB → 3.1GB |
热缓存数字是真正的故事。一旦 Turbopack 构建了你的项目一次,增量重建会快得多。对于我们的内容密集型项目 C(使用带有数千个静态生成页面的无头 CMS),从 6 分钟多下降到缓存构建中的 2 分钟以下意义重大。这是在 CI 分钟上节省的真实成本。
内存使用改进也很有意义。项目 A 有时在具有 webpack 的较小 CI 运行者上遇到 OOM 错误。该问题通过 Turbopack 消失了。

你需要了解的重大变化
以下是我们在迁移期间实际记录的所有内容。Next.js 16 升级指南涵盖了其中一些,但几个出乎我们的意料。
1. 自定义 Webpack 配置
这是大问题。如果你的 next.config.js 中有 webpack 键,它不再工作。Turbopack 在 Next.js 配置中的 turbopack 下有自己的配置 API。并非所有内容都有 1:1 的映射。
// next.config.js — 之前(Next.js 15 和 webpack)
module.exports = {
webpack: (config) => {
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack'],
});
return config;
},
};
// next.config.js — 之后(Next.js 16 和 Turbopack)
module.exports = {
turbopack: {
rules: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
},
};
2. 移除同步请求 API
Next.js 15 弃用了对 cookies()、headers()、params 和 searchParams 的同步访问。Next.js 16 完全删除了它们。如果你忽视了这些弃用警告,你会遇到大麻烦。
// 之前 — 这在 Next.js 16 中会崩溃
export default function Page({ params }) {
const { slug } = params;
return <div>{slug}</div>;
}
// 之后
export default async function Page({ params }) {
const { slug } = await params;
return <div>{slug}</div>;
}
这看起来很琐碎,但它在我们的项目中涉及超过 200 个组件。我们编写了一个 codemod 来处理其中大部分内容(下面会详细介绍)。
3. 不再支持 React 18
Next.js 16 需要 React 19。如果你有依赖项固定到 React 18,它们需要更新。到 2025 年中期,大多数维护良好的库都有 React 19 支持,但我们确实遇到了几个落后者。
4. Node.js 18 已删除
最低是 Node.js 20。更新你的 Docker 镜像、CI 配置和 .nvmrc 文件。
5. next/image 变化
onLoadingComplete 属性完全移除(从 Next.js 14 起已弃用)。改用 onLoad。镜像优化管道也使用新的基础库,这意味着你的缓存优化镜像将在首次请求时重新生成。
我们的分步迁移过程
以下是我们遵循的实际过程。我们先尝试项目 B(最小的),然后处理 A 和 C。
第 1 步:审计依赖项
在触及 Next.js 之前,我们审计了每个依赖项的 React 19 和 Node.js 20 兼容性。我们使用了一个简单的脚本:
npx npm-check-updates --target latest --filter '/react|next/'
我们也手动检查了我们最关键的包:我们的无头 CMS SDK、认证库和 UI 组件库。
第 2 步:更新 Node.js
我们将 .nvmrc 更新为 20.18.0(当时的最新 LTS),更新了 Dockerfile,并验证了 CI 运行者。简单但容易忘记。
第 3 步:先升级 React
npm install react@19 react-dom@19 @types/react@19 @types/react-dom@19
我们在这里运行了完整的测试套件,然后继续。React 19 有自己的重大变化(删除了必须使用 forwardRef,ref 现在是常规属性,use() 钩子是稳定的)。我们单独修复了这些问题,所以我们没有同时调试 React 和 Next.js 问题。
第 4 步:运行 Next.js Codemod
Next.js 提供升级 codemod,处理许多机械更改:
npx @next/codemod@latest upgrade
这自动处理了约 70% 的异步 API 迁移。它不完美——它在我们一些更复杂的服务器组件模式上遇到困难——但它为我们节省了数小时。
第 5 步:升级 Next.js
npm install next@16
第 6 步:迁移 next.config.js
这是项目 A 中耗时最多的步骤,该项目有大量 webpack 自定义。我将在下一部分中介绍具体翻译。
第 7 步:迭代修复构建错误
我们运行了 next build 并逐个处理错误。来自 Turbopack 的错误消息实际上比 webpack 在大多数情况下更好——更具体,文件路径更清晰,建议更有帮助。
第 8 步:视觉回归测试
我们使用 Playwright 进行 E2E 测试,并运行我们的视觉回归套件以捕获任何渲染差异。我们发现了两个问题:CSS 排序差异(Turbopack 处理 CSS 导入的顺序与 webpack 略有不同)和动态导入未正确代码分割。
第 9 步:性能验证
我们比较了迁移前后的 Lighthouse 分数和核心网络生命体征。每个项目都得到改进或保持中立。没有回归。
Webpack 配置转换
本部分适用于具有自定义 webpack 配置的团队。以下是常见模式如何转换为 Turbopack。
自定义加载器
// 自定义加载器的 Turbopack 等价物
module.exports = {
turbopack: {
rules: {
'*.md': {
loaders: ['raw-loader'],
as: '*.js',
},
'*.graphql': {
loaders: ['graphql-tag/loader'],
as: '*.js',
},
},
},
};
模块别名
// 解析别名的工作方式类似
module.exports = {
turbopack: {
resolveAlias: {
'old-package': 'new-package',
// 你也可以指向本地文件
'@legacy/utils': './src/utils/legacy.ts',
},
},
};
不翻译的内容
一些 webpack 插件根本没有 Turbopack 等价物:
webpack.DefinePlugin—— 在 next.config.js 中使用env或直接使用环境变量BundleAnalyzerPlugin—— 从 v16 开始,@next/bundle-analyzer包适用于 Turbopack,但输出格式更改了- 通过
splitChunks的自定义块分割 —— Turbopack 自动处理这个并不暴露相同级别的控制。老实说,默认值对大多数项目都很好。 webpack.IgnorePlugin—— 使用resolveAlias指向空模块
处理第三方包
一些包在迁移期间造成了问题:
@sentry/nextjs —— 对 Turbopack 兼容性需要 v9+。早期版本集成到 webpack 内部。升级很直接但需要配置更改。
next-intl —— 更新到最新版本后工作良好。插件 API 适应得很干净。
@vanilla-extract/next-plugin —— 这是项目 B 上我们最大的麻烦。Vanilla Extract 的 webpack 插件直到他们的 2.0 版本才有 Turbopack 等价物。我们要么等待,要么考虑替代方案。我们等了。
桶文件包 —— 任何从单个索引文件导出数百个组件的包(看你,图标库)现在被树摇得更积极。这是件好事,但我们看到一个动态引用的图标没有被包括。我们从基于字符串的图标查找切换到直接导入,这是更好的实践。
CSS 和 Tailwind 考虑
如果你使用 Tailwind CSS(我们大多数项目都这样做),迁移基本上是无痛的。Tailwind v4 与 Turbopack 配合得很好。但有几点需要注意:
CSS 导入顺序
Turbopack 以确定性但与 webpack 不同的顺序处理 CSS 导入。如果你依赖导入顺序来实现特异性(你不应该,但让我们诚实——我们最终都会这样),你可能会看到视觉差异。我们有一个项目,其中全局重置被组件 CSS 模块覆盖,因为导入顺序翻转了。
解决方案是在我们的 CSS 中显式使用 @layer,这是我们一开始就应该做的。
CSS 模块
CSS 模块的工作方式完全相同。不需要更改。生成的类名看起来不同(实际上更短),但这只是表面上的,除非你做了奇怪的事情,比如在测试中针对生成的类名。
PostCSS
PostCSS 配置文件仍然受尊重。你的 postcss.config.js 继续工作。这里不需要更改。
部署和 CI 管道更新
我们的部署目标主要是 Vercel 和 AWS(通过 SST/OpenNext)。以下是变化的内容:
Vercel:自动检测 Next.js 16 并使用 Turbopack。构建缓存集成开箱即用。我们在 Vercel 上的构建时间下降甚至比本地 CI 更剧烈,因为 Vercel 与 Turbopack 缓存层有深度集成。项目 C 在 Vercel 上从 ~8 分钟下降到 ~2.5 分钟。
AWS/OpenNext:需要更新到 OpenNext 4.x,添加了 Turbopack 输出支持。输出格式略有更改—— .next 目录结构已重新组织——所以任何引用特定文件路径的构建后脚本都需要更新。
Docker 构建:如果你在 Docker 中构建 Next.js,将基础镜像更新为 Node 20+ 并意识到 Turbopack 的缓存目录(.next/cache/turbopack)应包含在你的 Docker 层缓存策略中。我们为此添加了一个特定的 COPY 层。
# 为 Turbopack 优化 Docker 层缓存
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
# Turbopack 的缓存挂载
RUN --mount=type=cache,target=/app/.next/cache \
npm run build
何时不应该迁移
我不想把这描绘成全是玫瑰。有合理的理由现在留在 Next.js 15:
- 大量 webpack 插件依赖:如果你的构建依赖于 3 个以上自定义 webpack 插件,且没有 Turbopack 等价物,迁移成本可能不值得。
- 具有共享 webpack 配置的 Monorepo:如果你在 monorepo 中的 Next.js 和非 Next.js 项目间共享 webpack 配置,分割该配置是额外工作。
- 稳定性要求:Next.js 16.0 是稳定的,但 16.1 和 16.2 修复了真实的错误。在迁移任何无法容忍停机时间的东西之前,我会等待至少 16.2+。
- 遗留依赖卡在 React 18:如果关键依赖没有添加 React 19 支持,无论如何你都被阻止了。
对于进行 无头 CMS 开发的团队,迁移通常更顺利,因为 CMS 驱动的网站倾向于具有更简单的构建配置。
常见问题
在 Next.js 16 中 Turbopack 生产的稳定性足够吗? 是的。Turbopack 已经开发了三年多,在数百万 Next.js 项目的开发模式中经历了实战测试,并在 Next.js 15.3-15.5 期间进行了生产构建的扩展测试。自 16.0 版本发布以来,我们在多个客户网站上以生产方式运行它,没有任何打包器相关的问题。也就是说,如果你在 16.0 上,升级到 16.2+ 以解决几个边缘情况错误。
我仍然可以在 Next.js 16 中使用 webpack 吗? 不能作为主要打包器。Turbopack 是 Next.js 16 中唯一支持的打包器。如果你绝对需要 webpack,你需要停留在 Next.js 15,它将通过 2026 年初接收安全补丁。Vercel 已明确表示 Next.js 中对 webpack 的支持已结束。
Turbopack 与 webpack 的生产构建快多少? 在冷构建(无缓存)上,我们看到 20-30% 的改进。在热/缓存构建上,改进跳到 50-70%。确切的数字在很大程度上取决于项目大小、路由数量和发生的静态生成量。内存使用在我们所有项目中也一致下降 20-30%。
我需要为 Turbopack 重写我的 next.config.js 吗?
如果你在 next.config.js 中有自定义 webpack 配置,是的——这些块需要转换为 turbopack 配置格式。如果你仅使用标准 Next.js 配置选项(图像、重定向、重写、环境变量),它们的工作方式完全相同。迁移工作量与你有多少自定义 webpack 配置成正比。
我现有的 CI/CD 管道会在 Next.js 16 中工作吗?
大多是的。要更新的主要事项是:Node.js 版本(最低 20)、任何引用 webpack 特定输出文件的脚本,以及任何针对 .next/cache/webpack 的缓存策略。你会想要缓存 .next/cache/turbopack。如果你部署到 Vercel,一切都自动处理。
Turbopack 支持 webpack 在 Next.js 中的所有相同功能吗? 对于 Next.js 特定功能,是的—— App Router、Pages Router、API 路由、中间件、ISR、SSG、SSR 都工作。对于自定义 webpack 配置,大约有 90% 的功能奇偶性。剩余的差距主要是围绕利基 webpack 插件和高度自定义的块分割策略。检查 Turbopack 兼容性文档以了解你的具体用例。
我应该迁移到 Next.js 16 还是考虑 Astro 之类的替代方案? 这取决于你的用例。如果你构建具有复杂状态管理的高度交互应用,Next.js 16 是一个强有力的选择,Turbopack 改进使 DX 显著更好。如果你构建内容繁重的网站,最少交互,Astro 仍然是一个具有其部分水合模型的优秀替代方案。我们用两者都在构建,并根据项目要求选择。如果你不确定,与我们联系,我们可以帮你评估。
迁移中等规模的 Next.js 15 应用到 16 需要的最少时间是多少? 对于典型的中等规模应用(50-200 条路由、标准依赖、最小自定义 webpack 配置),预算 2-4 个开发者工作日。这包括依赖项更新、异步 API 迁移、测试和部署验证。如果你有大量自定义 webpack 配置或遗留依赖,可能需要一周或更长时间。我们在 Social Animal 的团队提供 迁移服务,如果你宁愿不在基础架构工作上烧掉你的团队的冲刺。