首页 / Next.js 16 入门教程 / 缓存机制

Next.js 16 入门教程

缓存机制

本教程共 42 篇 · 第 11 篇 · 更新于 2026-07-30 · 约 9 分钟阅读

Next.jsNext.js 16 入门教程缓存use cacheCache ComponentsPPR

11. 缓存机制

本节目标:理解 Next.js 的缓存模型,学会用 "use cache" 指令控制数据缓存,掌握 Partial Prerendering 的工作原理。

缓存的价值

缓存是为了避免重复工作。获取数据、计算结果这些操作做一次就行,结果存起来供后续请求使用。

Next.js 的缓存分几个层级:

  1. 请求记忆化:同一次请求内,相同 fetch 只执行一次
  2. 数据缓存:跨请求缓存数据(use cache
  3. 完整路由缓存:缓存整个路由的渲染结果
  4. 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:彻底失效,下次请求阻塞直到获取新数据
预设stalerevalidateexpire
default5 分钟15 分钟永不过期
seconds30 秒1 秒60 秒
minutes5 分钟1 分钟1 小时
hours5 分钟1 小时1 天
days5 分钟1 天1 周
weeks5 分钟1 周30 天
max5 分钟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()
  • searchParams
  • params(没有 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:

  1. 静态内容:自动包含在静态 shell 中
  2. 缓存内容use cache):包含在静态 shell 中
  3. Suspense fallback:包含在静态 shell 中
  4. 动态内容:流式加载
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> 包裹,构建时会报错。

小结

这一章我们深入学习了缓存机制:

  1. 开启 cacheComponents: true 启用新缓存模型
  2. "use cache" 缓存数据或 UI
  3. cacheLife 控制缓存有效期
  4. cacheTag 给缓存打标签
  5. 运行时 API 用 <Suspense> 包裹
  6. Partial Prerendering 混合静态和动态内容

下一章,我们来学习数据重新验证。