Next.js 15+ 异步请求 API、默认缓存策略反转与 PPR(部分预渲染)架构是如何工作的?
Next.js 15 是 App Router 架构走向成熟的分水岭版本,它彻底重构了 请求上下文 API 的同步/异步契约、默认缓存(Caching)策略,并引入了 PPR(Partial Prerendering,部分预渲染) 渲染模型。
一、 为什么 Next.js 15 将 cookies()、headers()、params 和 searchParams 全部改为异步(Async)?
在 Next.js 13/14 中,cookies()、headers() 和路由 params 是同步读取的,这迫使服务端必须等整个 HTTP 请求头与上下文解析完毕才能开始准备渲染。改为**返回 Promise(必须 await)**后:
- 为 PPR 部分预渲染铺平道路:只要组件还没执行到
await cookies()这一行,所有前置的静态组件外壳可以在构建期或请求到达的 0ms 瞬间直接从 CDN 流式发出! - 明确的动态渲染边界:一旦代码在
<Suspense>内部执行了await cookies()或await headers(),Next.js 就精确地只把该<Suspense>子树标记为动态流式空洞(Dynamic Hole),而不会连累整页退化为动态 SSR。
二、 Next.js 14 vs Next.js 15 默认缓存策略反转对比表
| 缓存场景 | Next.js 14 默认行为(激进缓存,易出陈旧数据坑) | Next.js 15+ 默认行为(显式Opt-in缓存,符合直觉) |
|---|---|---|
fetch() 请求 |
默认 cache: 'force-cache'(永久缓存) |
默认 no-store(每次请求拉取最新数据);需缓存时显式传 cache: 'force-cache' 或 next: { revalidate: N } |
GET Route Handlers (app/api/**/route.ts) |
默认被当作静态文件在构建期缓存! | 默认 dynamic(每次动态执行);需静态化时显式声明 export const dynamic = 'force-static' |
| Client Router Cache(客户端路由缓存) | 页面段默认在浏览器内存缓存 30 秒 | 页面段默认 staleTime: 0(导航前进时始终获取最新服务端数据,仅浏览器后退/前进复用缓存) |
三、 Next.js 15 异步 API 与 use cache / PPR 代码实战
import { Suspense } from 'react'
import { cookies } from 'next/headers'
// Next.js 15+:params 与 searchParams 均为 Promise,必须 await 解包
export default async function OrderPage({
params,
searchParams,
}: {
params: Promise<{ id: string }>
searchParams: Promise<{ tab?: string }>
}) {
const { id } = await params
const { tab = 'detail' } = await searchParams
return (
<section>
{/* 静态预渲染头部:构建期直接固化进 HTML 外壳 */}
<h1>订单详情 #{id}(当前页签:{tab})</h1>
{/* 动态流式区域:仅此 Suspense 内部读取 Cookie,不阻塞外层静态壳秒开 */}
<Suspense fallback={<p>正在校验用户专属优惠...</p>}>
<UserDiscountBanner orderId={id} />
</Suspense>
</section>
)
}
async function UserDiscountBanner({ orderId }: { orderId: string }) {
const cookieStore = await cookies()
const sessionToken = cookieStore.get('session_id')?.value
const discount = await fetchUserDiscount(orderId, sessionToken)
return <div className="banner">专属抵扣:¥{discount}</div>
}