首页 / Next.js 16 入门教程 / 高级缓存策略

Next.js 16 入门教程

高级缓存策略

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

Next.jsNext.js 16 入门教程缓存cacheLifeCDNstale-while-revalidateISR

39. 高级缓存策略

本节目标:掌握 Next.js 的高级缓存配置,理解 cacheLife 的三个时间维度,学会设计合理的缓存生命周期。

缓存生命周期(cacheLife)

Next.js 16 引入了 cacheLife 配置,用三个时间维度精确控制缓存行为:

属性说明
stale客户端缓存多久不检查服务端
revalidate服务端多久重新验证一次
expire过期数据最多保留多久,之后转为动态渲染

这三个时间的关系:stale < revalidate < expire

配置自定义缓存 Profile

// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  cacheComponents: true,
  cacheLife: {
    blog: {
      stale: 3600,    // 客户端缓存 1 小时
      revalidate: 900, // 服务端每 15 分钟重新验证
      expire: 86400,   // 最多保留 1 天
    },
  },
}

export default nextConfig

在代码中使用:

import { cacheLife } from 'next/cache'

export async function getBlogPosts() {
  'use cache'
  cacheLife('blog')
  const data = await fetch('/api/posts')
  return data
}

工作流程

  1. 用户 A 请求页面,数据被缓存
  2. 接下来 1 小时内(stale),用户 A 再次访问直接用缓存,不发请求
  3. 15 分钟后(revalidate),用户 B 访问时返回过期缓存,后台重新验证
  4. 1 天后(expire),缓存彻底过期,下次请求实时渲染

不使用 Cache Components 的缓存

如果你的项目还没启用 cacheComponents,可以用传统的缓存方式:

fetch 缓存

// 强制缓存
const data = await fetch('https://...', { cache: 'force-cache' })

// 设置重新验证时间
const data = await fetch('https://...', { next: { revalidate: 3600 } })

unstable_cache

缓存非 fetch 的异步函数:

import { unstable_cache } from 'next/cache'

export const getCachedUser = unstable_cache(
  async (id: string) => {
    return db.select().from(users).where(eq(users, id))
  },
  ['user'], // 缓存键
  {
    tags: ['user'],    // 用于按需重新验证
    revalidate: 3600,  // 1 小时后过期
  }
)

路由段配置

// 强制动态渲染
export const dynamic = 'force-dynamic'

// 强制静态渲染
export const dynamic = 'force-static'

// 设置默认重新验证时间
export const revalidate = 3600

按需重新验证

revalidatePathrevalidateTag 的完整用法见 §12。这里补充一点:在不启用 Cache Components 时,可以给 fetch 请求打标签,再用 revalidateTag 按需刷新:

// 给 fetch 请求打标签(非 Cache Components 场景)
const data = await fetch('https://api.example.com/posts', {
  next: { tags: ['posts'] },
})
// 数据变更后按标签重新验证
revalidateTag('posts', 'max')

请求去重

在同一个渲染过程中,相同的请求会自动去重。但如果用 ORM 直接查数据库,需要手动处理:

import { cache } from 'react'

export const getPost = cache(async (id: string) => {
  return await db.query.posts.findFirst({
    where: eq(posts.id, parseInt(id)),
  })
})

预加载数据

preload 模式提前发起数据请求:

import { cache } from 'react'
import 'server-only'

export const getItem = cache(async (id: string) => {
  // 查询逻辑
})

export const preload = (id: string) => {
  void getItem(id) // 不等待结果,只触发请求
}
export default async function Page({ params }) {
  const { id } = await params
  preload(id) // 提前开始加载
  const isAvailable = await checkIsAvailable() // 做其他事

  return isAvailable ? <Item id={id} /> : null
}

CDN 缓存

Next.js 的缓存响应会自动带上 cache-control 头,CDN 可以根据这些头做缓存:

  • 静态页面:public, max-age=31536000, immutable
  • ISR 页面:public, max-age=0, s-maxage=60, stale-while-revalidate

自定义缓存头

// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  async headers() {
    return [
      {
        source: '/blog/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, max-age=3600, s-maxage=86400',
          },
        ],
      },
    ]
  },
}

export default nextConfig

缓存调试

设置环境变量可以看到缓存命中情况:

NEXT_PRIVATE_DEBUG_CACHE=1

响应头 x-nextjs-cache 的值:

  • HIT:命中缓存
  • STALE:返回过期缓存,后台重新验证
  • MISS:未命中,重新渲染
  • REVALIDATED:按需重新验证

最佳实践

  1. 静态内容用长缓存:营销页面、文档可以缓存很久
  2. 用户数据用短缓存:个人信息、购物车要频繁更新
  3. 用标签做精细控制:不要整个站点一起重新验证
  4. 监控缓存命中率:太低说明缓存策略有问题
  5. 多实例部署用共享缓存:文件系统缓存是单实例的

缓存是性能优化的核心手段。理解 stale-while-revalidate 模型,能帮你在数据新鲜度和响应速度之间找到最佳平衡。