Nx 工作区中 Next.js 15 升级到 Next.js 16 的完整迁移指南:基于 Nx 22.2 自动化迁移指令的实战解析
【免费下载链接】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
Next.js 16 引入了大量破坏性变更(Async Request APIs、Turbopack 默认化、Middleware 重命名为 Proxy 等),在 Nx 多项目工作区中手动逐项目升级成本极高。本文以 Nx 官方随迁移生成器发布的 ai-instructions-for-next-16.md 为骨架,完整解析全部 13 类破坏性变更的修改要点、可复制的代码示例与验证流程,并结合 Nx 仓库中的迁移注册机制与 Agent 执行框架,帮助你系统、安全地完成升级。读完本文,你将掌握从项目识别、依赖升级、分类改写、构建验证到回退排障的完整实战方案。
一、这份迁移指令在 Nx 中的定位与工作机制
在 Nx 仓库中,这份文档并不是一份普通的散落说明,而是被正式注册为迁移生成器的"提示词(prompt)"。查看 packages/next/migrations.json 可以看到:
"update-22-2-0-create-ai-instructions-for-next-16": { "cli": "nx", "version": "22.2.0-beta.1", "requires": { "next": ">=16.0.0" }, "description": "Create AI Instructions to help migrate users workspaces to Next.js 16.", "prompt": "./dist/src/migrations/update-22-2-0/ai-instructions-for-next-16.md" }同一版本还通过packageJsonUpdates声明了配套的依赖升级目标(见 migrations.json):next升级到~16.0.1、eslint-config-next升级到^16.0.1。
从源码结构看,Nx 在packages/nx/src/command-line/migrate/agentic/下实现了整套"Agent 化迁移"机制(如 prompt-migration.ts),迁移生成器的prompt字段会被读取并注入到迁移任务的系统上下文中,交给 LLM/Agent 按指令逐条执行。这正是本文档"Notes for LLM Execution"一节存在的意义:它不仅是给人看的升级手册,更是给 Agent 看的、可逐步执行的任务说明书。
在动手之前,请先理解本迁移的适用范围:目标是把 Nx 工作区中基于 Next.js 15 的项目升级到 Next.js 16,工作方式是按破坏性变更类别逐项系统推进,而不是一次性盲目改动。
二、迁移前检查清单(Pre-Migration Checklist)
1. 识别所有 Next.js 项目
nx show projects --with-target build | xargs -I {} nx show project {} --json | jq -r 'select(.targets.build.executor | contains("next")) | .name'或者直接搜索 Next.js 配置文件:
find . -name "next.config.*" -not -path "*/node_modules/*"nx show projects是 Nx 22 起推荐的项目清单命令,配合nx show project <name> --json可以精确筛选出 build target 使用 next 相关 executor(如@nx/next:build或@nx/next/plugin推断出的任务)的项目。注意:@nx/next:buildexecutor 在仓库中已被标记为弃用并将随 Nx v24 移除,建议通过nx g @nx/next:convert-to-inferred迁移到@nx/next/plugin推断式插件(参见 packages/next/src/executors/build/schema.json),但无论哪种方式,升级到 Next.js 16 都需要完成本文的改动。
2. 更新依赖包
npm install next@latest react@latest react-dom@latest npm install -D @types/react @types/react-dom # if using TypeScript3. 核验最低环境要求
- Node.js 20.9+(Node.js 18 不再受支持)
- TypeScript 5.1.0+
- 浏览器支持:Chrome 111+、Edge 111+、Firefox 111+、Safari 16.4+
三、按类别的迁移步骤
1. 异步请求 API(Async Request APIs,最大破坏性变更)
这是 Next.js 16 影响面最大的变更:所有动态请求 API 都变为异步。
需要重点检索的模式:
- 服务端组件中的
cookies()用法 - 服务端组件中的
headers()用法 draftMode()用法- page、layout、route handler 和 metadata 文件中的
params - page 组件中的
searchParams
1.1 使用 params 的 Page 组件
// BEFORE (Next.js 15) export default function Page({ params }) { const { slug } = params; return <h1>{slug}</h1>; } // AFTER (Next.js 16) export default async function Page(props) { const { slug } = await props.params; return <h1>{slug}</h1>; }操作清单:
- 将所有使用
params的 page 组件改为 async - 在访问
props.params前添加await - 如适用,更新 TypeScript 类型
1.2 使用 searchParams 的 Page 组件
// BEFORE (Next.js 15) export default function Page({ searchParams }) { const query = searchParams.q; return <Results query={query} />; } // AFTER (Next.js 16) export default async function Page(props) { const searchParams = await props.searchParams; const query = searchParams.q; return <Results query={query} />; }操作清单:
- 将所有使用
searchParams的 page 组件改为 async - 在访问
props.searchParams前添加await
1.3 使用 params 的 Layout 组件
// BEFORE (Next.js 15) export default function Layout({ children, params }) { const { locale } = params; return <div>// BEFORE (Next.js 15) export async function GET(request, { params }) { const { id } = params; return Response.json({ id }); } // AFTER (Next.js 16) export async function GET(request, props) { const { id } = await props.params; return Response.json({ id }); }1.5 cookies() 与 headers()
// BEFORE (Next.js 15) import { cookies, headers } from 'next/headers'; export default function Page() { const cookieStore = cookies(); const headersList = headers(); const theme = cookieStore.get('theme'); const userAgent = headersList.get('user-agent'); return <div>...</div>; } // AFTER (Next.js 16) import { cookies, headers } from 'next/headers'; export default async function Page() { const cookieStore = await cookies(); const headersList = await headers(); const theme = cookieStore.get('theme'); const userAgent = headersList.get('user-agent'); return <div>...</div>; }1.6 draftMode()
// BEFORE (Next.js 15) import { draftMode } from 'next/headers'; export default function Page() { const { isEnabled } = draftMode(); return <div>{isEnabled ? 'Draft' : 'Published'}</div>; } // AFTER (Next.js 16) import { draftMode } from 'next/headers'; export default async function Page() { const { isEnabled } = await draftMode(); return <div>{isEnabled ? 'Draft' : 'Published'}</div>; }1.7 带 params 的 generateMetadata
// BEFORE (Next.js 15) export async function generateMetadata({ params }) { const { slug } = params; return { title: slug }; } // AFTER (Next.js 16) export async function generateMetadata(props) { const { slug } = await props.params; return { title: slug }; }1.8 自动化迁移
运行 Next.js 官方 codemod 完成自动化改写:
npx @next/codemod@canary upgrade latest生成类型辅助工具以获得更安全的迁移(Next.js 15.5+):
npx next typegen这会生成PageProps、LayoutProps和RouteContext类型辅助。它也是后续解决 params 类型报错的关键手段。
2. 图像生成函数(Image Generation Functions)
检索模式:generateImageMetadata、opengraph-image 或 twitter-image 文件中的default function Image。
// BEFORE (Next.js 15) export function generateImageMetadata({ params }) { const { slug } = params; return [{ id: '1' }]; } export default function Image({ params, id }) { const slug = params.slug; return new ImageResponse(/* ... */); } // AFTER (Next.js 16) export async function generateImageMetadata({ params }) { const { slug } = await params; return [{ id: '1' }]; } export default async function Image({ params, id }) { const { slug } = await params; const imageId = await id; return new ImageResponse(/* ... */); }操作清单:
- 将
generateImageMetadata函数改为 async - 将 Image 组件改为 async
- 对
params和id的访问都添加await
3. Sitemap 生成
检索模式:带id参数的sitemap函数。
// BEFORE (Next.js 15) export default async function sitemap({ id }) { const start = id * 50000; // ... } // AFTER (Next.js 16) export default async function sitemap({ id }) { const resolvedId = await id; const start = resolvedId * 50000; // ... }4. Turbopack 配置
Turbopack 现在是开发环境的默认打包器。
检索模式:package.json scripts 中的--turbo或--turbopack标志、next.config 中的turbopack配置。
4.1 移除显式 Turbopack 标志
// BEFORE (Next.js 15) { "scripts": { "dev": "next dev --turbo" } } // AFTER (Next.js 16) - Turbopack is default { "scripts": { "dev": "next dev" } }4.2 需要时回退到 Webpack
{ "scripts": { "build": "next build --webpack" } }4.3 将 Turbopack 配置移出 experimental
// BEFORE (Next.js 15) const nextConfig = { experimental: { turbopack: {/* options */}, }, }; // AFTER (Next.js 16) const nextConfig = { turbopack: {/* options */}, };4.4 更新 Sass 导入(Turbopack 特有)
/* BEFORE */ @import '~bootstrap/dist/css/bootstrap.min.css'; /* AFTER - Remove tilde prefix */ @import 'bootstrap/dist/css/bootstrap.min.css';操作清单:
- 从 scripts 中移除
--turbo与--turbopack标志 - 将
turbopack配置从experimental提升到根级 - 移除 Sass 导入中的波浪号(
~)前缀 - 需要 Webpack 时添加
--webpack标志
5. Middleware 重命名为 Proxy
检索模式:middleware.ts或middleware.js文件。
# Rename the file mv middleware.ts proxy.ts// BEFORE (middleware.ts) export function middleware(request) { // ... } // AFTER (proxy.ts) export function proxy(request) { // ... }配置项更新:
// BEFORE { skipMiddlewareUrlNormalize: true; } // AFTER { skipProxyUrlNormalize: true; }重要:proxy中不再支持 Edge runtime,它现在使用 Node.js runtime。
操作清单:
- 将
middleware.ts/js重命名为proxy.ts/js - 将导出的函数名从
middleware改为proxy - 更新配置项名称
- 从 proxy 文件中移除 Edge runtime 用法
6. 并行路由的 default.js 要求
检索模式:app 目录中以@开头的目录(并行路由插槽)。
所有并行路由插槽现在都要求有显式的default.js文件。
// Create app/@modal/default.tsx for each parallel route slot import { notFound } from 'next/navigation'; export default function Default() { notFound(); // or return null }操作清单:
- 找出所有并行路由插槽(
app/@*/) - 为每个没有
default.tsx的插槽创建该文件
7. 图像优化变更
7.1 带查询字符串的本地图像
// Now requires explicit configuration <Image src="/assets/photo?v=1" alt="Photo" width="100" height="100" />// next.config.js module.exports = { images: { localPatterns: [ { pathname: '/assets/**', search: '?v=1', }, ], }, };7.2 默认值变更
如果业务需要旧的默认行为,在next.config.js中补充:
module.exports = { images: { // minimumCacheTTL changed from 60 to 14400 seconds minimumCacheTTL: 60, // Value 16 removed from default imageSizes imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], // qualities now defaults to [75] only qualities: [50, 75, 100], // Local IP now blocked by default dangerouslyAllowLocalIP: true, // only for private networks // Maximum redirects changed from unlimited to 3 maximumRedirects: 5, }, };7.3 弃用的 images.domains
// BEFORE - Remove this module.exports = { images: { domains: ['example.com'], }, }; // AFTER - Use remotePatterns instead module.exports = { images: { remotePatterns: [ { protocol: 'https', hostname: 'example.com', }, ], }, };操作清单:
- 为带查询字符串的图像添加
localPatterns - 将
images.domains迁移到images.remotePatterns - 按需审查并更新默认值
8. 缓存 API 更新
8.1 移除 unstable_ 前缀
// BEFORE (Next.js 15) import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag, } from 'next/cache'; // AFTER (Next.js 16) import { cacheLife, cacheTag } from 'next/cache';8.2 新的缓存函数
revalidateTag 配合 cacheLife profile:
'use server'; import { revalidateTag } from 'next/cache'; export async function updateArticle(articleId: string) { revalidateTag(`article-${articleId}`, 'max'); }updateTag(新增):
'use server'; import { updateTag } from 'next/cache'; export async function updateUserProfile(userId: string, profile: Profile) { await db.users.update(userId, profile); updateTag(`user-${userId}`); }refresh(新增):
'use server'; import { refresh } from 'next/cache'; export async function markNotificationAsRead(notificationId: string) { await db.notifications.markAsRead(notificationId); refresh(); }操作清单:
- 从
cacheLife和cacheTag的导入中移除unstable_前缀 - 考虑使用新的
updateTag和refresh函数
9. React Compiler 支持
React Compiler 现已稳定并被支持:
// next.config.ts const nextConfig = { reactCompiler: true, }; export default nextConfig;安装插件:
npm install -D babel-plugin-react-compiler注意:启用 React Compiler 后编译时间会变长。
10. 滚动行为覆盖(Scroll Behavior Override)
Next.js 不再在导航期间覆盖scroll-behavior: smooth。如需恢复之前的行为:
// app/layout.tsx export default function RootLayout({ children }) { return ( <html lang="en"># Run migration codemod npx @next/codemod@canary next-lint-to-eslint-cli .从next.config.js中移除:
// Remove this { eslint: { } }操作清单:
- 运行 ESLint 迁移 codemod
- 从
next.config.js中移除eslint配置 - 将 CI 脚本改为直接使用
eslint,而不是next lint
12. 特性移除(Feature Removals)
12.1 AMP 支持被移除
- 所有 AMP API 已被删除
- 移除
useAmphook 用法 - 移除
amp配置项 - 删除 AMP 专用页面
12.2 运行时配置被移除
// BEFORE - Remove these module.exports = { serverRuntimeConfig: { dbUrl: process.env.DATABASE_URL }, publicRuntimeConfig: { apiUrl: '/api' }, };服务端配置迁移——直接使用环境变量:
// Use environment variables directly async function fetchData() { const dbUrl = process.env.DATABASE_URL; return await db.query(dbUrl, 'SELECT * FROM users'); }客户端配置迁移:
# .env.local NEXT_PUBLIC_API_URL="/api"'use client'; export default function Component() { const apiUrl = process.env.NEXT_PUBLIC_API_URL; // ... }12.3 devIndicators 选项被移除
从next.config.js中移除以下选项:
appIsrStatusbuildActivitybuildActivityPosition
12.4 experimental.dynamicIO 重命名
// BEFORE { experimental: { dynamicIO: true; } } // AFTER { cacheComponents: true; }12.5 unstable_rootParams 被移除
该 API 已删除,请等待未来 minor 版本中的替代 API。
操作清单:
- 移除所有 AMP 相关代码
- 将运行时配置迁移到环境变量
- 移除弃用的 devIndicators 选项
- 将
dynamicIO重命名为cacheComponents
13. 开发相关变更
13.1 并行 dev 与 build
开发环境现在输出到.next/dev(与 build 输出分离)。更新 Turbopack tracing 命令:
npx next internal trace .next/dev/trace-turbopack四、迁移后验证(Post-Migration Validation)
1. 逐项目构建
# Build each Next.js project individually nx run PROJECT_NAME:build2. 启动开发服务器
# Start dev server to verify Turbopack works nx run PROJECT_NAME:serve3. 构建所有受影响项目
# Build all affected projects nx affected -t build4. 运行完整验证
# Run full CI validation nx prepush5. 复查迁移清单
- 所有异步请求 API 已更新
- 所有使用 params 的 page/layout 组件已改为 async
- Turbopack 配置已更新
- Middleware 已重命名为 proxy
- 并行路由已有 default.js 文件
- 图像配置已更新
- 缓存导入已更新(移除 unstable_ 前缀)
- AMP 代码已移除
- 运行时配置已迁移到环境变量
- ESLint 配置已迁移
- 所有项目构建成功
- 开发服务器正常启动
五、常见问题与解决方案
| 问题 | 解决方案 |
|---|---|
| "cookies() expects to be called in a synchronous context" | 将函数改为 async 并await cookies() |
| "params should be awaited before accessing properties" | 在访问props.params前添加await |
| 使用 Turbopack 时构建失败 | 为 build 脚本添加--webpack标志,然后逐步解决 Turbopack 兼容性 |
| 重命名后 Middleware 不生效 | 确保文件和函数都已从middleware重命名为proxy |
| 并行路由不渲染 | 为并行路由插槽添加default.tsx文件 |
| 带查询字符串的图像无法加载 | 为这些图像添加localPatterns配置 |
| params 类型相关 TypeScript 报错 | 运行npx next typegen生成类型辅助,并使用PageProps、LayoutProps类型 |
六、需要审查的文件清单
创建一份所有待审查文件的清单:
# Find all pages with potential params usage find . -path "*/app/*" -name "page.tsx" -o -name "page.ts" | xargs grep -l "params\|searchParams" # Find all layouts find . -path "*/app/*" -name "layout.tsx" -o -name "layout.ts" # Find all route handlers find . -path "*/app/*" -name "route.ts" -o -name "route.tsx" # Find middleware files find . -name "middleware.ts" -o -name "middleware.js" # Find files using cookies/headers rg "from 'next/headers'" --type ts --type tsx # Find next.config files find . -name "next.config.*" -not -path "*/node_modules/*" # Find parallel routes find . -path "*/app/@*" -type d七、大型工作区的迁移策略
- 分阶段迁移:从一个小项目开始,验证通过后再扩大范围
- 使用 codemod:运行
npx @next/codemod@canary upgrade latest完成自动化修复 - 生成类型:运行
npx next typegen获得类型安全的迁移 - 频繁运行测试:每次配置变更后,运行受影响的测试
- 记录问题:跟踪记录项目特定问题及其解决方案
在 Nx 工作区中,还可以利用nx affected系列命令把验证范围精确收敛到受影响的图(project graph)节点上:改完一批文件后执行nx affected -t build,Nx 只会构建受影响的项目及其依赖链,大幅缩短反馈回路。
八、迁移期间的常用命令
# Find all Next.js projects nx show projects --with-target build # Build specific project nx build PROJECT_NAME # Serve specific project nx serve PROJECT_NAME # Build all affected nx affected -t build # View project details nx show project PROJECT_NAME --web # Clear Nx cache if needed nx reset九、LLM / Agent 执行注意事项(Notes for LLM Execution)
原文档的最后一节专门面向自动化执行者,如果你的迁移由 LLM/Agent 驱动,请严格遵循以下执行纪律:
- 系统性推进:完成一个类别后再进入下一个类别,不要跳步
- 每次变更后测试:不要积压所有变更后再统一验证
- 保持用户知情:在推进每个小节时同步汇报进度
- 及时处理错误:构建失败时立即修复,不要带着错误继续
- 优先使用 codemod:让
@next/codemod处理重复的 async/await 改写 - 优先处理破坏性变更:先聚焦异步 API,它影响面最大
- 创建有意义的提交:将相关变更分组提交,配以清晰的提交信息
- 使用 TodoWrite 工具:通过任务清单跟踪迁移进度,保持可见性
结语
Next.js 16 的这次升级,破坏性变更集中在异步化(Async Request APIs、图像生成、sitemap、缓存函数)、默认打包器切换(Turbopack)、命名重构(Middleware→Proxy、dynamicIO→cacheComponents)与一批功能移除(AMP、runtimeConfig、next lint)上。在 Nx 工作区中,这份由 migrations.json 注册的迁移指令文档既是人工升级的操作手册,也是 Nx Agent 化迁移(packages/nx/src/command-line/migrate/agentic/)的执行蓝本。按照"识别项目 → 更新依赖 → 分类改写 → 逐项目验证 → 全量回归"的路径推进,配合 codemod 与nx affected的增量验证,即可在控制风险的前提下完成整个工作区的升级。
【免费下载链接】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),仅供参考