作为React生态中最流行的全栈框架,Next.js每次版本更新都牵动着无数开发者的心。Next.js 15带来了自App Router推出以来最重要的变化——全面拥抱React 19,同时对多个核心API进行了异步化改造。这些变化不仅影响着代码的编写方式,更深刻改变了我们对服务端渲染的认知。
本文将基于官方文档,系统性地解析Next.js 15的所有重要变化。无论你是正在考虑升级的现有项目开发者,还是准备学习Next.js的新人,这篇文章都将帮助你全面理解新版本的特性和升级策略。
# 升级准备工作
# 环境要求
在升级到Next.js 15之前,需要确保你的开发环境满足以下要求:
- Node.js 18.17.0 或更高版本
- 如果使用TypeScript,需要安装最新的
@types/react和@types/react-dom - 建议使用pnpm 8+、npm 10+或yarn 1.22+
# 一键升级
Next.js官方提供了Codemod工具,可以自动完成大部分迁移工作。在项目根目录下运行以下命令:
# 使用pnpm
pnpm dlx @next/codemod@canary upgrade latest
# 使用npm
npx @next/codemod@canary upgrade latest
# 使用yarn
yarn dlx @next/codemod@canary upgrade latest
# 使用bun
bunx @next/codemod@canary upgrade latest
# 手动升级
如果你 prefer 手动升级,需要同时更新Next.js和React相关依赖:
# pnpm
pnpm add next@latest react@latest react-dom@latest eslint-config-next@latest
# npm
npm install next@latest react@latest react-dom@latest eslint-config-next@latest
# yarn
yarn add next@latest react@latest react-dom@latest eslint-config-next@latest
注意:如果遇到peer dependencies警告,可能需要手动指定React版本或使用
--force/--legacy-peer-deps参数。这在Next.js 15和React 19正式稳定后将不再需要。
# React 19全面支持
# 版本要求
Next.js 15要求React和ReactDom的最低版本为19。这意味着你可以直接使用React 19带来的所有新特性,包括:
- useActionState:取代原来的useFormState,提供更强大的表单状态管理能力
- useFormStatus增强:新增
data、method、action等属性 - Actions:支持在服务端定义可调用函数
# useFormState迁移到useActionState
useFormState已被useActionState替代,虽然前者仍然可用,但已标记为废弃。useActionState提供了更丰富的API,包括直接读取pending状态的能力:
import { useActionState } from 'react'
async function submitForm(prevState, formData) {
// 表单提交逻辑
const result = await fetch('/api/submit', {
body: formData
})
return result.json()
}
export default function Form() {
const [state, submitAction, isPending] = useActionState(submitForm, null)
return (
<form action={submitAction}>
{/* 表单字段 */}
<button type="submit" disabled={isPending}>
{isPending ? '提交中...' : '提交'}
</button>
</form>
)
}
# useFormStatus增强
在React 19中,useFormStatus增加了更多有用的属性:
import { useFormStatus } from 'react-dom'
function SubmitButton() {
const { pending, data, method, action } = useFormStatus()
return (
<button type="submit" disabled={pending}>
{pending ? '提交中...' : '提交'}
</button>
)
}
提示:如果你还未升级到React 19,
useFormStatus仍然只提供pending属性。
# 异步Request APIs:重大架构变化
这是Next.js 15最重要的变化之一。原本同步的动态API(如cookies、headers、draftMode)现在需要异步调用。这些API依赖运行时信息,异步化后能更好地支持React 19的并发渲染能力。
# cookies异步化
import { cookies } from 'next/headers'
// Next.js 14(同步)
const cookieStore = cookies()
const token = cookieStore.get('token')
// Next.js 15(异步)
const cookieStore = await cookies()
const token = cookieStore.get('token')
如果你暂时不想修改所有代码,可以使用类型转换保持同步调用(仅作为过渡方案):
import { cookies, type UnsafeUnwrappedCookies } from 'next/headers'
// 临时同步用法(不推荐,仅作过渡)
const cookieStore = cookies() as unknown as UnsafeUnwrappedCookies
// 开发模式下会收到警告
const token = cookieStore.get('token')
# headers异步化
import { headers } from 'next/headers'
// Next.js 14(同步)
const headersList = headers()
const userAgent = headersList.get('user-agent')
// Next.js 15(异步)
const headersList = await headers()
const userAgent = headersList.get('user-agent')
# draftMode异步化
import { draftMode } from 'next/headers'
// Next.js 14(同步)
const { isEnabled } = draftMode()
// Next.js 15(异步)
const { isEnabled } = await draftMode()
# params和searchParams异步化
在Next.js 15中,layout和page组件的params和searchParams都变成了Promise类型。
# 异步Page组件
// Next.js 14
type Params = { slug: string }
type SearchParams = { [key: string]: string | string[] | undefined }
export default async function Page({
params,
searchParams,
}: {
params: Params
searchParams: SearchParams
}) {
const { slug } = params
const { query } = searchParams
}
// Next.js 15
type Params = Promise<{ slug: string }>
type SearchParams = Promise<{ [key: string]: string | string[] | undefined }>
export default async function Page(props: {
params: Params
searchParams: SearchParams
}) {
const params = await props.params
const searchParams = await props.searchParams
const { slug } = params
const { query } = searchParams
}
# 同步Layout组件(使用use hook)
如果你需要保持Layout组件同步,可以使用React 19的use hook:
import { use } from 'react'
type Params = Promise<{ slug: string }>
export default function Layout(props: {
children: React.ReactNode
params: Params
}) {
const params = use(props.params)
const { slug } = params
return <div>{slug}</div>
}
# Route Handlers中的params
// Next.js 14
type Params = { slug: string }
export async function GET(request: Request, segmentData: { params: Params }) {
const params = segmentData.params
}
// Next.js 15
type Params = Promise<{ slug: string }>
export async function GET(request: Request, segmentData: { params: Params }) {
const params = await segmentData.params
}
# Fetch请求默认行为变化
# 默认不再缓存
Next.js 15中,fetch请求默认不再自动缓存。这意味着你需要在使用时明确指定缓存策略:
# 批量启用缓存
如果你希望整个layout或page中的fetch请求都默认缓存,可以使用fetchCache配置:
# Route Handlers的GET请求
GET请求默认不再缓存。如果需要缓存,需要显式设置:
# 客户端路由缓存策略调整
# 页面导航不再复用缓存
不过,浏览器后退前进导航和共享布局仍然会复用缓存。