自2022年公布以来,我们一直在关注 Turbopack 的成熟度,从 Next.js 14 开始在开发模式下运行它,并谨慎地关注 CI 中的"turbo"标志。当 Next.js 16 在 2025 年初发布,Turbopack 作为默认生产打包工具时,我们知道迁移不能再等待了。我们有三个客户项目在 Next.js 15 上,需要升级,我将为你讲述每一个重大变化、我们遇到的陷阱,以及我们看到的性能数据。

这不是对发布说明的重述。这是当我们在具有真实复杂性的真实代码库上运行 next build 与 Turbopack 时实际发生的情况。

目录

Next.js 16 Turbopack Production Builds: What Changed and How We Migrated

为什么 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 的可能性。

构建性能基准

以下是我们三个迁移项目的真实数据。这些不是综合基准 — 它们来自在我们的 CI 管道中运行的实际客户应用程序,GitHub Actions(Ubuntu,4 vCPU,16GB RAM 运行程序)。

指标 项目 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 Turbopack Production Builds: What Changed and How We Migrated - architecture

你需要知道的重大变化

以下是在我们迁移期间实际损坏的所有内容的运行列表。Next.js 16 升级指南涵盖了其中一些,但有些让我们措手不及。

1. 自定义 Webpack 配置

这是大问题。如果你在 next.config.js 中有一个 webpack 键,它就不再工作了。Turbopack 在 Next.js 配置中的 turbopack 下有自己的配置 API。并非所有内容都有 1:1 的映射。

// next.config.js — 之前 (Next.js 15 with webpack)
module.exports = {
  webpack: (config) => {
    config.module.rules.push({
      test: /\.svg$/,
      use: ['@svgr/webpack'],
    });
    return config;
  },
};
// next.config.js — 之后 (Next.js 16 with Turbopack)
module.exports = {
  turbopack: {
    rules: {
      '*.svg': {
        loaders: ['@svgr/webpack'],
        as: '*.js',
      },
    },
  },
};

2. 同步请求 API 已删除

Next.js 15 已弃用对 cookies()headers()paramssearchParams 的同步访问。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 prop 完全被删除(自 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),更新 Dockerfiles,并验证 CI 运行程序。简单但容易忘记。

步骤 3:首先升级 React

npm install react@19 react-dom@19 @types/react@19 @types/react-dom@19

我们在这里运行了完整的测试套件,然后再继续。React 19 有它自己的重大变化(删除了 forwardRef 作为必需的东西,ref 现在是一个常规 prop,use() hook 是稳定的)。我们隔离地修复了这些问题,所以我们不是同时调试 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 最耗时的步骤,项目 A 有大量 webpack 自定义。我将在下一部分介绍具体的转换。

步骤 7:迭代修复构建错误

我们运行了 next build 并逐一解决错误。Turbopack 的错误消息实际上比 webpack 的在大多数情况下要好 — 更具体,文件路径更清晰,建议也更清晰。

步骤 8:视觉回归测试

我们使用 Playwright 进行 E2E 测试并运行了我们的视觉回归套件以捕捉任何渲染差异。我们发现了两个问题:CSS 排序差异(Turbopack 处理 CSS 导入的顺序与 webpack 略有不同)和没有正确代码分割的动态导入。

步骤 9:性能验证

我们比较了迁移前后的 Lighthouse 分数和 Core Web Vitals。每个项目都改进或保持中立。没有回归。

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 与 Turbopack 缓存层有深度集成,我们在 Vercel 上的构建时间下降幅度甚至比本地 CI 更大。项目 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+ 个没有 Turbopack 等效物的自定义 webpack 插件,迁移成本可能不值得。
  • 具有共享 webpack 配置的 Monorepo:如果你在 monorepo 中的 Next.js 和非 Next.js 项目之间共享 webpack 配置,拆分该配置是额外的工作。
  • 稳定性要求:Next.js 16.0 是稳定的,但 16.1 和 16.2 修复了真实的错误。在迁移你不能容忍停机时间的任何东西之前,我会等待至少 16.2+。
  • 卡在 React 18 上的遗留依赖:如果关键依赖项还没有添加 React 19 支持,无论如何你都被阻止了。

对于从事 无头 CMS 开发 的团队,迁移通常更顺利,因为 CMS 驱动的网站往往有更简单的构建配置。

常见问题

Turbopack 在 Next.js 16 中对生产足够稳定吗? 是的。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 支持已经完成。

与 webpack 相比,Turbopack 对于生产构建快多少? 在冷构建(无缓存)上,我们看到了 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 团队提供 迁移服务,如果你不想让你的团队在基础设施工作上燃烧冲刺。