← 返回 夜间开发者办公桌上闪烁的琥珀色终端,磨损的键盘旁放着空咖啡杯,背景是雨迹斑斑的窗户

升级到 Next.js 16:我的真实迁移笔记

三周前我坐在 Seahawk 办公室的周四下午,相当确信从 Next.js 15 升级到 16 需要四十分钟最多。结果花了整个下午。说实话?更新日志并没有为那些真正会崩溃的部分做好准备。

到目前为止我已经建立并发布了超过 12,000 个网站。我这么说不是为了炫耀,而是因为我做过足够多的迁移,知道何时官方升级指南在粉饰什么。Next.js 16 粉饰了一些东西。所以这是我的真实笔记,按照我希望有人曾为我写下的方式写的。

---

为什么我决定升级

Turbopack。简短的答案就是这样。

长的答案是我有两个网站还在 Next.js 14,一个已经在 15 上,我一直在关注 Turbopack 的发展约十八个月。版本 16 是 Turbopack 在 next dev 上默认启用的第一个版本。这不是小事。在去年为一个时尚客户做的中等规模电商项目中,开发冷启动时间很慢,首次加载需要 12 到 18 秒。如果 Turbopack 真的能大幅减少这个时间,迁移的痛苦就值得了。

剧透:它确实减少了。在同类项目中,我看到冷启动 3 到 4 秒。这是真实的。

但到达那里的路有一些尖锐的边缘。

---

实际升级命令(以及首先要运行什么)

在碰任何东西之前,对当前配置进行完整审计。我现在虔诚地使用 npx @next/codemod@latest。它不会捕捉所有内容,但会捕捉明显的重命名和已弃用的 API 调用。运行它,提交输出,然后更新 package.json

升级本身:

  1. package.json 中将 next、react 和 react-dom 更新到目标版本
  2. 运行 npm install(或 pnpm install,如果你理智的话,我已经用了两年 pnpm)
  3. 运行代码模块:npx @next/codemod@latest upgrade
  4. 启动 next dev 并在接触任何组件之前阅读每个警告
  5. 在修复组件问题之前修复配置问题,顺序很重要

代码模块步骤是我看到人们绊倒的地方。他们跳过它,遇到三个单独的错误,花一小时寻找本应自动修复的东西。不要跳过它。

---

next.config.js 变更会咬你一口

这是我第一个真正的惊喜。配置 API 的变化比我预期的要大。

`experimental` 块现在更精简了

过去一两年内在实验块中的几个标志要么被提升为稳定(并移到顶层),要么完全删除。我亲自遇到的两个:

  • experimental.appDir 已移除。App Router 现在是默认设置。如果你的配置中还有这个,会抛出警告(某些设置下会直接报错)。
  • experimental.serverComponentsExternalPackages 提升为配置顶层的 serverExternalPackages

第二个问题在我使用 Prisma 的网站上犯过。构建在服务器包上静默失败,我花了大概四十分钟盯着错误的文件才发现问题。在假设是组件问题之前,从头到尾检查一遍你的 next.config.js

Turbopack 配置搬到了新位置

如果你之前有自定义的 Webpack 配置,现在要切换到 Turbopack(你会的,因为它现在是开发的默认选项),你需要知道 next.config.js 中的 webpack() 函数在 Turbopack 运行时不适用。它只在 next build 时适用,而那时仍然使用 Webpack。

如果你有自定义的 SVG 处理(我在大多数项目上用 SVGR)、自定义模块别名或任何 loader 配置,这会很重要。你需要在新的 turbopack 配置块中重复配置这些。Next.js Turbopack 配置文档对这方面其实写得不错,在假设有问题之前值得一读。

---

React 19 兼容性:隐藏的地雷

Next.js 16 以 React 19 作为对等依赖发布。如果你从 Next.js 14 升级(跳过 15),你一次性跳了两个 React 主版本。这就是事情变得复杂的地方。

我碰到的最大问题是旧的第三方组件库。我有个客户网站用了一个表格库,它内部使用 ReactDOM.render()。React 19 完全移除了这个 API,它在 React 18 中就被弃用了,但还能用。在 19 中,它直接报错。

我在一个周二上午花时间处理这个。错误信息不会直接指向这个库;它只是告诉你 ReactDOM.render 不再支持。运行 npm ls react 看看你依赖树里哪些包有冲突的 React 对等依赖声明。这一个命令就救了我大概两小时的猜测。

几个值得了解的 React 19 特定模式:

  • forwardRef 不再是传递 ref 的必需品;ref 现在是普通 prop。使用 forwardRef 的旧组件仍然能用,但你会看到弃用警告。
  • use() 现在稳定了,对客户端组件中的异步数据真的很有用。对于直接的数据获取,我开始优先用它而不是 useEffect + state。
  • Server Actions 有更严格的类型要求。如果你在 action 签名中有任何松散的类型,TypeScript 现在会找到它。

---

App Router:缓存行为发生了什么变化

这个很微妙,如果不注意会在生产环境中抓住你。

在 Next.js 14 中,Server Components 内的 fetch() 默认被积极缓存。你必须用 { cache: 'no-store' } 禁用它。在 Next.js 15 中他们反转了这个(fetch 默认不缓存),Next.js 16 继续这个方向,增加了更多显式控制。

如果你一次性从 14 迁到 16(就像我对我的一个网站做的),依赖旧默认缓存行为的页面会开始在每个请求上做实时获取。对某些页面没问题。对其他的,会砸爆你的 API,摧毁响应时间。

The fix is explicit: use export const revalidate = 3600 (or whatever interval makes sense) at the route segment level, or pass { next: { revalidate: 3600 } } directly in your fetch call. The Next.js caching documentation has a solid breakdown of what caches what and when.

我用一个快速的 grep fetch( 审计了受影响网站的每条数据获取路由,并添加了显式缓存声明。花了大概两小时,但值得;响应时间从平均约 800ms 降到修复后的约 120ms。

---

Turbopack 实战:优点和烦人的地方

我直说吧:Turbopack 很厉害。冷启动时间好得多。热模块替换在大多数更改上感觉几乎是即时的。对日常开发来说这是个有意义的生活质量升级。

但有粗糙的地方。

什么还不能用

我做这些迁移的时候,少数几样东西在 Turbopack 开发中仍然没有完全支持:

  • 一些 Webpack 特定的 loader 还没有 Turbopack 等价物。SVGR 需要配置改动(Turbopack 规则语法与 Webpack 的 module.rules 不同)。
  • 自定义 Babel 转换。Turbopack 仅使用 SWC。如果你的项目有 .babelrc babel.config.js 包含自定义插件,那些不会运行。这是一个已知限制,Vercel 团队在他们的 Turbopack 文档中对此很坦诚。
  • 一些 PostCSS 插件组合在开发中表现异常。我在 Tailwind v4 + 自定义 PostCSS 设置中看到过这种情况,修复方法是明确指定 PostCSS 插件顺序。

`--turbopack` 标志现在不需要了

由于 Turbopack 是 version 16 中 next dev 的默认值,你不再需要 --turbopack 标志。如果你在 version 15 的实验中在 package.json 脚本中有它,不会造成任何问题,但它是冗余的。整理你的脚本。

---

TypeScript 和 ESLint 配置更新

两件让我困扰的日常维护事项。

Next.js 16 要求 TypeScript 5.x。如果你仍在使用 TypeScript 4.x(一些较旧的项目确实如此),你需要单独升级它。在开始任何其他操作之前,运行 npx tsc --version

ESLint 配置的情况也改变了。Next.js 16 附带 ESLint 9 支持,ESLint 9 使用扁平配置格式(eslint.config.js),而不是旧的 .eslintrc 格式。如果你仍在使用旧格式,Next.js 会优雅地回退,但你会看到一条警告。我在迁移过程中顺便将我的两个项目迁移到了扁平配置。一旦克服了最初的障碍,它确实更简洁。

---

我的迁移清单(按顺序)

这是我会提供给我团队中任何执行此升级的人的内容:

  1. 在进行任何操作之前,备份你的当前配置文件和锁定文件
  2. 检查你的 Node.js 版本,Next.js 16 要求 Node 18.18 或更高版本
  3. 在当前版本上首先运行 npx @next/codemod@latest upgrade
  4. package.json 中提升 next、react、react-dom 版本并安装
  5. 查看 next.config.js 以了解升级或移除的实验性标志
  6. 运行 npm ls react 以发现第三方库冲突
  7. 在代码库中使用 grep 搜索 fetch( 并审计缓存声明
  8. 检查任何需要 Turbopack 等效方案的 .babelrc 或 Webpack 特定加载器
  9. 启动开发服务器,在修改任何组件之前读完所有警告
  10. 在部署到任何地方之前在本地运行生产构建(next build
  11. 部署到暂存环境并执行完整的手动冒烟测试

最后这一步听起来很明显。但我见过有人跳过暂存,直接推送到生产环境,说是"小"升级。一个导致你的主页在每次请求上都命中实时 API 的缓存行为变化不是什么小事。

---

FAQ

Next.js 16 对生产环境来说足够稳定吗?

是的,对大多数用例来说。Turbopack 用于开发的变化是最大的转变,由于生产构建仍然使用 Webpack,你的实际部署输出受影响的程度不如你的开发体验。缓存行为变化是更大的生产顾虑,如果你方法得当,那些是直接可以审计的。

我需要同时升级到 React 19 吗?

严格来说,Next.js 16 最低支持 React 18,但新功能(如稳定的 use() hook 和 ref-as-prop 变化)需要 React 19。如果你的项目有很多第三方依赖,值得在承诺同时升级 React 19 之前检查兼容性。React 19 升级指南值得与 Next.js 迁移文档一起阅读。

我的自定义 Webpack 配置在 Turbopack 下消失了。我该怎么办?

你的 Webpack 配置在 next build 时仍会运行。在开发模式下,你需要使用 next.config.js 中的 turbopack 键复制相关部分。语法不同,特别是对于文件转换和别名。查看官方 Turbopack 配置参考,如果你的 Webpack 配置复杂的话,预计需要一到两个小时。

Turbopack 实际上快了多少?

在我测试过的项目中:冷启动从 12-18 秒降至 3-4 秒。组件变化的 HMR 在大多数情况下从 1-3 秒降至 200ms 以下。这些是粗略数字,会因项目规模而异,但对于任何超出玩具项目的东西,差异都是显而易见的。

---

这次升级值得进行。Turbopack 的开发速度本身改变了你在大型 Next.js 代码库中的工作感受。只是要睁大眼睛了解缓存变化和第三方库兼容性检查,这两件事是大部分时间实际花费的地方。

一次升级一个网站。我就是这样做的。

← 返回