缓存机制
本教程共 42 篇 · 第 11 篇 · 更新于 2026-07-30 · 约 9 分钟阅读
11. 缓存机制
本节目标:理解 Next.js 的缓存模型,学会用
"use cache"指令控制数据缓存,掌握 Partial Prerendering 的工作原理。
缓存的价值
缓存是为了避免重复工作。获取数据、计算结果这些操作做一次就行,结果存起来供后续请求使用。
Next.js 的缓存分几个层级:
- 请求记忆化:同一次请求内,相同
fetch只执行一次 - 数据缓存:跨请求缓存数据(
use cache) - 完整路由缓存:缓存整个路由的渲染结果
- CDN 缓存:边缘节点缓存静态资源
启用 Cache Components
Next.js 16 的缓存模型需要显式开启:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
开启后,路由和 GET Route Handler 会遵循相同的预渲染模型。
use cache 指令
"use cache" 是新增的指令,用来缓存异步函数或组件的返回值。
数据级缓存
缓存一个获取数据的函数:
import { cacheLife } from 'next/cache'
export async function getUsers() {
'use cache'
cacheLife('hours')
return db.query('SELECT * FROM users')
}
不同参数会产生不同的缓存条目:
export async function getUser(id: string) {
'use cache'
cacheLife('hours')
return db.query('SELECT * FROM users WHERE id = ?', id)
}
// getUser('1') 和 getUser('2') 分别缓存
UI 级缓存
缓存整个组件:
export default async function Page() {
'use cache'
cacheLife('hours')
const users = await db.query('SELECT * FROM users')
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
Note文件顶部加
"use cache",所有导出的函数都会被缓存。
cacheLife 详解
cacheLife 控制缓存的有效期。它接受预设名称或自定义配置:
// 用预设
cacheLife('hours')
// 自定义
cacheLife({
stale: 3600, // 1 小时后标记为过期
revalidate: 7200, // 2 小时后重新验证
expire: 86400, // 1 天后彻底失效
})
三个时间段的含义:
- stale:缓存过期但还能用,同时触发后台刷新
- revalidate:必须重新验证后才能用
- expire:彻底失效,下次请求阻塞直到获取新数据
| 预设 | stale | revalidate | expire |
|---|---|---|---|
default | 5 分钟 | 15 分钟 | 永不过期 |
seconds | 30 秒 | 1 秒 | 60 秒 |
minutes | 5 分钟 | 1 分钟 | 1 小时 |
hours | 5 分钟 | 1 小时 | 1 天 |
days | 5 分钟 | 1 天 | 1 周 |
weeks | 5 分钟 | 1 周 | 30 天 |
max | 5 分钟 | 30 天 | 1 年 |
cacheTag 标签
给缓存打标签,方便批量清除:
import { cacheTag } from 'next/cache'
export async function getProducts() {
'use cache'
cacheTag('products')
cacheLife('hours')
return db.query('SELECT * FROM products')
}
调用 revalidateTag('products', 'max') 可以清除所有带这个标签的缓存。
流式渲染未缓存的数据
如果组件需要每次请求都获取最新数据,不要用 "use cache",而是用 <Suspense>:
import { Suspense } from 'react'
async function LatestPosts() {
const data = await fetch('https://api.example.com/posts')
const posts = await data.json()
return (
<ul>
{posts.map((post: any) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
export default function Page() {
return (
<>
<h1>我的博客</h1>
<Suspense fallback={<p>加载文章...</p>}>
<LatestPosts />
</Suspense>
</>
)
}
<Suspense> 的 fallback 会立即显示,数据加载完后替换。
运行时 API 的处理
以下 API 依赖请求时的信息,不能直接在缓存组件中使用:
cookies()headers()searchParamsparams(没有generateStaticParams时)
必须用 <Suspense> 包裹:
import { cookies } from 'next/headers'
import { Suspense } from 'react'
async function UserGreeting() {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')?.value || 'light'
return <p>你的主题:{theme}</p>
}
export default function Page() {
return (
<Suspense fallback={<p>加载中...</p>}>
<UserGreeting />
</Suspense>
)
}
把运行时值传给缓存组件
可以提取运行时值,作为参数传给缓存函数:
export default function Page() {
return (
<Suspense fallback={<div>加载中...</div>}>
<ProfileContent />
</Suspense>
)
}
// 非缓存组件读取运行时数据
async function ProfileContent() {
const session = (await cookies()).get('session')?.value
return <CachedContent sessionId={session} />
}
// 缓存组件接收提取的值
async function CachedContent({ sessionId }: { sessionId: string }) {
'use cache'
const data = await fetchUserData(sessionId)
return <div>{data}</div>
}
sessionId 成为缓存 key 的一部分,不同用户有不同缓存。
Partial Prerendering (PPR)
开启 Cache Components 后,默认使用 Partial Prerendering:
- 静态内容:自动包含在静态 shell 中
- 缓存内容(
use cache):包含在静态 shell 中 - Suspense fallback:包含在静态 shell 中
- 动态内容:流式加载
export default function BlogPage() {
return (
<>
{/* 静态 - 预渲染 */}
<header>
<h1>博客</h1>
<nav>...</nav>
</header>
{/* 缓存 - 预渲染 */}
<BlogPosts />
{/* 动态 - 流式 */}
<Suspense fallback={<p>加载中...</p>}>
<UserPreferences />
</Suspense>
</>
)
}
构建时,静态和缓存内容生成 HTML。请求时,动态内容流式填入。
渲染流程
构建时,Next.js 遍历组件树:
| 组件类型 | 处理方式 |
|---|---|
use cache | 结果缓存,包含在静态 shell |
<Suspense> | fallback 包含在静态 shell,内容流式渲染 |
| 确定性操作(纯计算、同步 I/O) | 自动包含在静态 shell |
| 运行时 API | 必须用 <Suspense>,否则报错 |
如果组件访问了运行时数据但没用 <Suspense> 包裹,构建时会报错。
小结
这一章我们深入学习了缓存机制:
- 开启
cacheComponents: true启用新缓存模型 "use cache"缓存数据或 UIcacheLife控制缓存有效期cacheTag给缓存打标签- 运行时 API 用
<Suspense>包裹 - Partial Prerendering 混合静态和动态内容
下一章,我们来学习数据重新验证。