SuperAPI AI 工具集

next-cache-components

前端与建站

查看项目

更新于 10-16 04:18

技能介绍

用途

Cache Components 是 Next.js 16 引入的缓存机制,用于实现部分预渲染(PPR,Partial Prerendering),让同一个路由中同时包含静态、缓存和动态内容。它取代了旧版本中的 experimental.ppr 标志。

启用方式

在 next.config.ts 中设置:

import type {NextConfig} from 'next'

const nextConfig: NextConfig = {
  cacheComponents: true,
}

export default nextConfig

三类内容

启用后,页面内容分为三类:

  1. 静态(自动预渲染):同步代码、导入、纯计算,在构建时预渲染。
  2. 缓存(use cache):不需要每次请求都重新获取的异步数据。
  3. 动态(Suspense):必须保持新鲜的运行时数据,用 Suspense 包裹。

示例:

import {Suspense} from 'react'

export default function Page() {
  return (
    <>
      <BlogPosts /> {/* Cached */}
      <Suspense fallback={<p>Loading...</p>}>
        <UserPreferences /> {/* Dynamic - streams in */}
      </Suspense>
    </>
  )
}

async function UserPreferences() {
  const theme = (await cookies()).get('theme')?.value
  return <p>Theme: {theme}</p>
}

use cache 指令

可作用于三个层级:

  • 文件级:在文件顶部写 'use cache',整个页面被缓存。
  • 组件级:在异步组件内写 'use cache'。
  • 函数级:在异步函数内写 'use cache'。

缓存配置

内置配置:

  • 'use cache':默认,5 分钟 stale,15 分钟 revalidate
  • 'use cache: remote':平台提供的缓存(Redis、KV)
  • 'use cache: private':用于合规场景,允许运行时 API

cacheLife() 可设置自定义生命周期:

import {cacheLife} from 'next/cache'

async function getData() {
  'use cache'
  cacheLife('hours')
  return fetch('/api/data')
}

内置配置包括:'default'、'minutes'、'hours'、'days'、'weeks'、'max'。也可内联配置:

cacheLife({
  stale: 3600, // 1 hour - serve stale while revalidating
  revalidate: 7200, // 2 hours - background revalidation interval
  expire: 86400, // 1 day - hard expiration
})

缓存失效

  • cacheTag():为缓存内容打标签。
  • updateTag():立即失效,同一请求内即可看到新数据。
  • revalidateTag():后台重新验证,下一次请求看到新数据(stale-while-revalidate)。

示例:

'use server'

import {updateTag} from 'next/cache'

export async function updateProduct(id: string, data: FormData) {
  await db.products.update({where: {id}, data})
  updateTag(`product-${id}`)
}

运行时数据约束

在 use cache 内不能访问 cookies()、headers() 或 searchParams。解决方案是把这些值提取到外部,作为参数传入,参数会自动成为缓存键的一部分。

例外:使用 'use cache: private' 时允许访问运行时 API,适用于无法重构的合规场景。

缓存键生成

缓存键自动基于以下内容生成:

  • Build ID:部署时使所有缓存失效
  • Function ID:函数位置的哈希
  • 可序列化参数:props 成为键的一部分
  • 闭包变量:外层作用域的值也会被包含

完整示例

import {Suspense} from 'react'
import {cookies} from 'next/headers'
import {cacheLife, cacheTag} from 'next/cache'

export default function DashboardPage() {
  return (
    <>
      <header>
        <h1>Dashboard</h1>
      </header>
      <nav>...</nav>

      <Stats />

      <Suspense fallback={<NotificationsSkeleton />}>
        <Notifications />
      </Suspense>
    </>
  )
}

async function Stats() {
  'use cache'
  cacheLife('hours')
  cacheTag('dashboard-stats')

  const stats = await db.stats.aggregate()
  return <StatsDisplay stats={stats} />
}

async function Notifications() {
  const userId = (await cookies()).get('userId')?.value
  const notifications = await db.notifications.findMany({
    where: {userId, read: false},
  })
  return <NotificationList items={notifications} />
}

从旧版本迁移

旧配置 替代方案
experimental.ppr cacheComponents: true
dynamic = 'force-dynamic' 移除(默认行为)
dynamic = 'force-static' 'use cache' + cacheLife('max')
revalidate = N cacheLife({ revalidate: N })
unstable_cache() 'use cache' 指令

从 unstable_cache 迁移到 use cache

unstable_cache 在 Next.js 16 中已被 use cache 指令取代。启用 cacheComponents 后,将 unstable_cache 调用转换为 use cache 函数。

迁移前:

import {unstable_cache} from 'next/cache'

const getCachedUser = unstable_cache(async (id) => getUser(id), ['my-app-user'], {
  tags: ['users'],
  revalidate: 60,
})

迁移后:

import {cacheLife, cacheTag} from 'next/cache'

async function getCachedUser(id: string) {
  'use cache'
  cacheTag('users')
  cacheLife({revalidate: 60})
  return getUser(id)
}

主要差异:

  • 无需手动缓存键:use cache 会根据函数参数和闭包自动生成键,不再需要 unstable_cache 的 keyParts 数组。
  • 标签:用函数内的 cacheTag() 调用替代 options.tags。
  • 重新验证:用 cacheLife({ revalidate: N }) 或内置配置(如 cacheLife('minutes'))替代 options.revalidate。
  • 动态数据:unstable_cache 不支持在回调内使用 cookies() 或 headers(),use cache 有相同限制,但必要时可使用 'use cache: private'。

限制

  • 不支持 Edge runtime,需要 Node.js。
  • 不支持静态导出,需要服务器。
  • 非确定性值(Math.random()、Date.now())在 use cache 内只在构建时执行一次。

如需在缓存外获得请求时随机性:

import {connection} from 'next/server'

async function DynamicContent() {
  await connection() // Defer to request time
  const id = crypto.randomUUID() // Different per request
  return <div>{id}</div>
}