本节摘要:Next.js 迭代快,升级是常态。本节讲清两条升级路径:小版本升级(codemod 自动迁移)与 Pages Router → App Router 大迁移(架构级),以及升级前的准备、破坏性变更处理与验证流程。
阅读完本节,你应当能够:
npm install next@latest 一时爽,跑起来全是坑——Next.js 的大版本升级常带破坏性变更(API 移除、行为变化、配置项改名)。盲目升级轻则警告刷屏,重则页面全挂。升级是工程,需要准备、迁移、验证三步走。
直觉类比:升级框架像"老房翻新"——不能直接换地基(版本号),要先勘察(兼容性检查)、分批改造(codemod/手动迁移)、验收(测试+预览)——稳扎稳打,每步可回退。
💡 关键直觉:官方升级工具(codemod)能自动完成大部分"机械性迁移"——改 API 名、移动文件、更新配置。人力集中在"行为变化"的部分(渲染模型、数据获取语义)。先用工具清掉机械劳动,再人工处理语义变化。
# 查看升级指南 npx @next/codemod@latest --help # 自动迁移(示例:Next.js 13 → 14) npx @next/codemod@latest next-13-to-14 . # 单文件测试模式(先试跑) npx @next/codemod@latest next-13-to-14 ./pages/index.tsx
codemod 能自动处理:import 路径变更、API 重命名、配置项调整。
# 1. 先升级 React(Next.js 依赖 React 版本) npm install react@latest react-dom@latest # 2. 升级 Next.js npm install next@latest # 3. 跑 codemod 自动迁移 npx @next/codemod@latest <目标版本> . # 4. 检查破坏性变更 # 阅读官方升级指南(nextjs.org/docs/app/building-your-application/upgrading)
npm run lint # 代码规范 npm run test:unit # 单元测试 npm run build # 构建(类型检查 + 编译) npm run test:e2e # 端到端 # 手动验证核心流程(登录、CRUD、SEO)
验证不通过怎么办:逐个修复;修复不了用 git revert 回退——升级的每一步都可回滚。
| 维度 | Pages Router | App Router |
|---|---|---|
| 路由定义 | pages/xxx.jsx | app/xxx/page.tsx |
| 布局 | 手动 _app/_document | layout.tsx 自动嵌套 |
| 数据获取 | getServerSideProps 等 | 服务器组件 async 直接取 |
| 组件 | 默认客户端 | 默认服务器组件 |
| API | pages/api/xxx.js | app/api/xxx/route.ts |
第一步:路由文件迁移
# 页面 pages/about.jsx → app/about/page.tsx pages/posts/[id].jsx → app/posts/[id]/page.tsx # API pages/api/posts.js → app/api/posts/route.ts
第二步:数据获取迁移
// 旧:getServerSideProps export async function getServerSideProps() { const data = await fetchData(); return { props: { data } }; } export default function Page({ data }) { ... } // 新:服务器组件直接取数 export default async function Page() { const data = await fetchData(); return <div>{data}</div>; }
第三步:组件迁移
"use client";第四步:混合共存
// next.config.ts —— 迁移期间两套路由共存 const nextConfig = { // App Router 与 Pages Router 可共存,逐个迁移 };
渐进式原则:一次迁移一个页面/功能,每个都验证通过再继续——避免"大爆炸式"重构。
| 误区 | 现象 | 正解 |
|---|---|---|
| 直接改版本号 | 大量报错 | codemod + 手动迁移 |
| 忽略 React 版本 | 依赖不兼容 | 先升级 React |
| 一次迁移全部 | 出问题难定位 | 渐进式逐个迁移 |
| 忘检查行为变化 | 数据获取逻辑错了 | 读升级指南的 breaking changes |
| 升级后不验证 | 上线才发现挂 | lint+test+build+e2e 全跑 |
// 1. 旧代码 pages/posts/[id].jsx export async function getServerSideProps({ params }) { const res = await fetch(`/api/posts/${params.id}`); const post = await res.json(); return { props: { post } }; } export default function Post({ post }) { return <h1>{post.title}</h1>; } // 2. 新代码 app/posts/[id]/page.tsx export default async function PostPage({ params, }: { params: Promise<{ id: string }>; }) { const { id } = await params; const post = await fetch(`/api/posts/${id}`).then((r) => r.json()); return <h1>{post.title}</h1>; } // 3. 移动文件 git mv pages/posts/[id].jsx "app/posts/[id]/page.tsx"
对比:getServerSideProps + props 传递 → 服务器组件直接取数——代码更少、逻辑更直。迁移一个页面,理解 App Router 的数据流,其余页面照此办理。
升级 Next.js 时,除了机械性迁移(codemod 处理),更要留意"行为差异"——这些不会报错,但会让功能悄悄变样。
| 旧(Pages Router) | 新(App Router) | 差异 |
|---|---|---|
| getStaticProps 构建时执行 | 服务器组件默认静态 | 取数时机不同,注意副作用 |
| getServerSideProps 每请求执行 | dynamic force-dynamic | 显式声明 |
| 无自动缓存 | fetch 默认缓存 | 数据可能"不新鲜",需要 revalidate |
# 逐页面迁移,每步验证 1. npm run lint # 规范 2. npm run build # 类型 + 编译 3. npm run test # 单元 4. 手动验证页面 # 数据、交互、SEO 5. npx playwright test # 端到端
一句话:升级迁移最危险的不是"报错"而是"不报错但行为变了"——数据获取时机、组件默认值、缓存语义是三大高频差异点,迁移后务必手动验证数据流与交互。
问:升级后还能继续用 Pages Router 吗?
能。Next.js 支持两套 Router 共存,迁移期间可混合。但新页面建议直接用 App Router,逐步迁移旧页面,最终删除 pages/ 目录。
问:codemod 跑完还需要做什么?
codemod 处理机械性变更(API 名、import 路径)。剩下的"行为差异"(数据获取时机、组件默认值、缓存语义)需要人工审查——跑完 codemod 不代表迁移完成。
问:升级后构建报错怎么办?
逐个修复:先看类型错误(tsc),再看弃用警告(build 输出),最后跑测试。修复不了就 git revert 回退,再分批升级。
问:什么时候该升级?
不是"出新版就升"。评估:新版本是否修复你遇到的问题、是否有你需要的新特性、社区是否已稳定(等几个 patch 版本)。生产项目升级要谨慎,先在预览环境验证。