Nx 迁移指南:将 Next.js 从 14 升级到 15(异步 Request API、React 19 与缓存行为变更)
【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nx
本篇技术指南围绕 Nx 仓库中 packages/next/src/migrations/update-23-1-0 目录下的官方升级文档展开,系统讲解在 Nx 工作区中把 Next.js 项目从 14 升级到 15 的完整路径:先运行官方 codemod,再手工处理异步 Request API、React 19、缓存默认值等破坏性变更,最后通过nx命令逐个项目构建验证。读完本文,你将掌握 Nx 迁移机制如何自动联动这次升级、每一步的具体操作与代码改写模式,以及 Page Router 与 App Router 在此次升级中的差异处理。
升级背景:Nx 迁移机制如何承载 Next.js 14 → 15
Nx 通过 packages/next/migrations.json 注册每个版本的迁移(migration),update-23-1-0-create-ai-instructions-for-next-15正是为 Nx 23.1.0 准备的迁移项,其声明(见 migrations.json)包含两个关键字段:
prompt:指向 ai-instructions-for-next-15.md,这是面向 LLM/AI Agent 的逐步操作指令,用于指导自动执行升级;documentation:指向 upgrade-to-next-15.md,即本文所依据的人读版升级说明。
同时,迁移还通过packageJsonUpdates自动处理依赖版本(见 migrations.json):
"23.1.0": { "version": "23.1.0-beta.0", "requires": { "next": ">=14.0.0 <15.0.0" }, "packages": { "next": { "version": "~15.5.18", "alwaysAddToPackageJson": false }, "eslint-config-next": { "version": "^15.5.18", "alwaysAddToPackageJson": false } } }含义很明确:只有当工作区当前next版本满足>=14.0.0 <15.0.0时该迁移才会触发,并把next升到~15.5.18、eslint-config-next升到^15.5.18,保证两者版本匹配。这正是本次升级的第一步——依赖版本由 Nx 迁移自动完成,而代码层面的改动需要按下面的步骤处理。
总体策略:先 Codemod,再手工,最后逐项目构建
升级的核心工作流(与 ai-instructions-for-next-15.md 中的指令一致)是:
- 运行官方 codemod 自动改写大部分代码;
- 对 App Router 项目执行 React 18 → 19 迁移;
- 手工修复异步 Request API 的遗留点;
- 重新审视缓存依赖;
- 处理杂项变更;
- 用
nx命令逐个项目构建验证。
其中最重要的一条经验是:每次只处理一个项目,处理完立刻构建,把问题隔离在单个项目内,避免错误在全工作区叠加。
第一步:运行官方 Codemod
在项目根目录执行:
npx @next/codemod@canary upgrade 15三个值得注意的细节:
- 目标版本必须显式写
15而不是latest——latest标签在当前时间点已经解析到 16,直接使用会跳过本次 14→15 迁移的目标版本; @canary标签是 Next.js 官方发布升级 codemod 的渠道,因此需要带上该 tag 才能获取到升级工具;- codemod 能自动处理大部分异步 Request API 的重写,但执行后务必 review 完整 diff,确认每一处改写都符合预期,尤其是涉及业务逻辑的判断型代码。
codemod 处理不了的剩余点,就进入下一步手工修复。
第二步:React 19 升级(仅 App Router)
Next.js 15 的 App Router 强制要求 React 19;Page Router 项目可以继续停留在 React 18。因此:
- 对使用 App Router 的 Nx 项目,需要额外执行 React 18 → 19 的迁移(升级
react、react-dom及配套类型包); - 对纯 Page Router 项目,这一步可以整体跳过。
判断标准很简单:查看项目中是否使用app/目录(App Router)还是仅使用pages/目录(Page Router)。App Router 与 Page Router 的差异也贯穿本次升级的其他步骤,尤其是异步 API 的处理。
第三步:异步 Request API(本次升级的主要破坏性变更)
Next.js 15 将一组请求相关 API 改为异步,这是本次升级最主要的破坏性变更:
paramssearchParamscookiesheadersdraftMode
在 Next.js 14 中,它们是同步对象,直接解构即可使用;在 Next.js 15 中,它们都变成了 Promise,必须先await再读取。
改写模式:params/searchParams
官方升级文档给出了标准的 Before / After 对照(源自 upgrade-to-next-15.md):
升级前(app/blog/[slug]/page.tsx):
export default function Page({ params, searchParams }) { const { slug } = params; const query = searchParams.q; return <h1>{slug}</h1>; }升级后:
export default async function Page(props) { const { slug } = await props.params; const { q: query } = await props.searchParams; return <h1>{slug}</h1>; }改写要点:
- 将组件(或 Route Handler)声明为
async function; - 对
props.params、props.searchParams分别await; - 由于
searchParams现在必须经过await,解构时需注意把查询参数名与本地变量名对应好(上例中查询参数q被重命名为局部变量query); - 多个异步值可以合并解构(如
const { q: query } = await props.searchParams;),不必逐字段 await。
同样的写法适用于cookies/headers/draftMode
官方文档明确指出,这三个 API 的等待方式与params完全一致(见 upgrade-to-next-15.md):
const store = await cookies();凡是直接同步使用cookies()、headers()、draftMode()返回值的地方(例如在 Server Component 或 Route Handler 中读取请求头、Cookie、预览模式状态),都必须改为await之后再用。
例外:Page Router 的getServerSideProps/getStaticProps/getStaticPaths不受影响
异步化只作用于 App Router 的请求 API。Page Router 中三个数据获取函数的context.params保持同步,不需要任何改动:
getServerSidePropsgetStaticPropsgetStaticPaths
这是官方文档与配套 AI 指令都特别强调的边界,手工修复时不要误改这些代码。
第四步:缓存默认值变更
Next.js 15 收紧了默认缓存策略,以下三种场景不再默认缓存,如果你之前依赖它们的缓存行为,需要显式恢复:
| 场景 | Next.js 14 默认行为 | Next.js 15 默认行为 | 恢复缓存的方式 |
|---|---|---|---|
fetch请求 | 默认缓存 | 默认不缓存 | fetch(url, { cache: 'force-cache' }) |
| GET Route Handler | 默认静态缓存 | 默认不缓存 | export const dynamic = 'force-static' |
| 客户端导航 | 默认缓存 | 默认不缓存 | 按路由配置重新启用 |
fetch的显式恢复写法:
const data = await fetch(url, { cache: 'force-cache' });Route Handler 的显式恢复写法:
export const dynamic = 'force-static';这一变更的影响面比异步 API 更隐蔽:代码不报错,但运行时行为(缓存命中、静态化)悄然改变,因此升级后应重点回归涉及数据获取、SSG 的页面与接口。
第五步:杂项变更清单
官方文档与 AI 指令还列出了以下零散但必须处理的变更:
1.@next/font已移除,改用next/font
所有从@next/font导入字体的代码(如@next/font/google、@next/font/local)都要改为从next/font导入:
// before import { Inter } from '@next/font/google'; // after import { Inter } from 'next/font/google';2. Edge Runtime 名称变更
Route 配置中的runtime: 'experimental-edge'改为runtime: 'edge':
// before export const runtime = 'experimental-edge'; // after export const runtime = 'edge';3.next.config配置项平级化
两个原本位于experimental命名空间下的配置项被提升为顶层配置(ai-instructions-for-next-15.md):
| 升级前(experimental 下) | 升级后(顶层) |
|---|---|
experimental.bundlePagesExternals | bundlePagesRouterDependencies |
experimental.serverComponentsExternalPackages | serverExternalPackages |
4.NextRequest.geo与request.ip被移除
在 middleware 等场景中曾使用的NextRequest.geo和request.ip已不再可用,需要改为从请求头读取:
- IP:读取
x-forwarded-for请求头; - 地理位置:读取平台提供的 geo 请求头(例如部署平台注入的
x-vercel-ip-country等)。
从代码结构看,这些字段的移除意味着所有依赖内建 geo/IP 推断的中间件逻辑都需要重构为显式的请求头解析。
第六步:逐个项目构建验证
升级收尾阶段,用 Nx 命令验证每个项目及受影响范围(ai-instructions-for-next-15.md):
nx run PROJECT:build nx affected -t build,lint,testnx run PROJECT:build:针对单个 Next.js 项目构建,配合"一次一个项目"的策略定位问题;nx affected -t build,lint,test:基于 Nx 的依赖图分析,只对受升级影响的项目执行构建、Lint 与测试,全量验证升级没有破坏其他模块。
与 Nx 插件机制的关联
在 Nx 中,Next.js 项目的 target 由 packages/next/src/plugins/plugin.ts 中的createNodes动态推导。该插件以**/next.config.{ts,js,cjs,mjs}为匹配模式扫描工作区(见 plugin.ts),并读取每个项目的next.config内容来生成build、dev、start等 target(包括通过nextConfig.distDir推导输出目录,见 plugin.ts)。
这意味着本次升级中next.config的配置项重命名(如experimental.bundlePagesExternals→bundlePagesRouterDependencies)不仅是 Next.js 侧的要求,也会影响 Nx 插件对配置的解析与 target 推导结果——配置改完后,建议重新生成/检查 Nx 的项目图(nx graph)确认 target 输出与依赖关系正常,再执行构建验证。
升级自检清单
将上述内容浓缩为一份可执行的清单:
- 确认
next版本满足>=14.0.0 <15.0.0,由 Nx 迁移自动 bump 到~15.5.18(eslint-config-next同步到^15.5.18); - 运行
npx @next/codemod@canary upgrade 15并 review diff; - App Router 项目执行 React 18 → 19 迁移(Page Router 跳过);
- 手工修复
params/searchParams/cookies/headers/draftMode的await改写,组件与 Handler 改为async; - 确认 Page Router 的
getServerSideProps/getStaticProps/getStaticPaths未被误改; - 对依赖缓存的
fetch与 GET Route Handler 显式恢复cache: 'force-cache'与dynamic = 'force-static'; - 处理
@next/font、runtime: 'edge'、next.config重命名、middleware 中request.ip/NextRequest.geo的替代方案; - 逐项目执行
nx run PROJECT:build,最后nx affected -t build,lint,test全量验证。
遵循"codemod 优先、手工兜底、逐项目构建"的节奏,Next.js 15 的破坏性变更可以在 Nx 工作区中平稳落地。
【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考