cache-components

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
PROACTIVE ACTIVATION: Use this skill automatically when working in Next.js projects that have
cacheComponents: true
in their next.config.ts/next.config.js. When this config is detected, proactively apply Cache Components patterns and best practices to all React Server Component implementations.
DETECTION: At the start of a session in a Next.js project, check for
cacheComponents: true
in next.config. If enabled, this skill's patterns should guide all component authoring, data fetching, and caching decisions.
主动激活:当在next.config.ts/next.config.js中配置了
cacheComponents: true
的Next.js项目中工作时,自动启用此技能。检测到该配置后,主动将Cache Components模式和最佳实践应用于所有React Server Components实现。
检测机制:在Next.js项目会话开始时,检查next.config中是否存在
cacheComponents: true
。如果已启用,此技能的模式应指导所有组件编写、数据获取和缓存决策。

Next.js Cache Components

Next.js Cache Components

Auto-activation: This skill activates automatically in projects with
cacheComponents: true
in next.config.
自动激活:当项目的next.config中存在
cacheComponents: true
时,此技能自动激活。

Project Detection

项目检测

When starting work in a Next.js project, check if Cache Components are enabled:
bash
undefined
在Next.js项目中开始工作时,检查是否已启用Cache Components:
bash
undefined

Check next.config.ts or next.config.js for cacheComponents

检查next.config.ts或next.config.js中的cacheComponents配置

grep -r "cacheComponents" next.config.* 2>/dev/null

If `cacheComponents: true` is found, apply this skill's patterns proactively when:

- Writing React Server Components
- Implementing data fetching
- Creating Server Actions with mutations
- Optimizing page performance
- Reviewing existing component code

Cache Components enable **Partial Prerendering (PPR)** - mixing static HTML shells with dynamic streaming content for optimal performance. Cache Components also enable state preservation during navigation with React's `<Activity>` component, which can keep cached component trees mounted but hidden.
grep -r "cacheComponents" next.config.* 2>/dev/null

如果检测到`cacheComponents: true`,在以下场景中主动应用此技能的模式:

- 编写React Server Components
- 实现数据获取逻辑
- 创建带突变操作的Server Actions
- 优化页面性能
- 评审现有组件代码

Cache Components 支持**Partial Prerendering (PPR)**——将静态HTML外壳与动态流内容相结合,以实现最优性能。Cache Components还通过React的`<Activity>`组件在导航期间保留状态,该组件可以让缓存的组件树保持挂载但隐藏状态。

Philosophy: Code Over Configuration

设计理念:代码优先,配置次之

Cache Components represents a shift from segment configuration to compositional code:
Before (Deprecated)After (Cache Components)
export const revalidate = 3600
cacheLife('hours')
inside
'use cache'
export const dynamic = 'force-static'
Use
'use cache'
and Suspense boundaries
All-or-nothing static/dynamicGranular: static shell + cached + dynamic
Key Principle: Components co-locate their caching, not just their data. Next.js provides build-time feedback to guide you toward optimal patterns.
Cache Components代表了从分段配置组合式代码的转变:
之前(已废弃)之后(Cache Components)
export const revalidate = 3600
在'use cache'作用域内使用
cacheLife('hours')
export const dynamic = 'force-static'
使用'use cache'和Suspense边界
全静态或全动态的二选一模式细粒度控制:静态外壳 + 缓存内容 + 动态内容
核心原则:组件将缓存逻辑与数据逻辑内聚在一起,而不仅仅是数据。Next.js会在构建时提供反馈,引导你采用最优模式。

Core Concept

核心概念

┌─────────────────────────────────────────────────────┐
│                   Static Shell                       │
│  (Sent immediately to browser)                       │
│                                                      │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │
│  │   Header    │  │  Cached     │  │  Suspense   │  │
│  │  (static)   │  │  Content    │  │  Fallback   │  │
│  └─────────────┘  └─────────────┘  └──────┬──────┘  │
│                                           │         │
│                                    ┌──────▼──────┐  │
│                                    │  Dynamic    │  │
│                                    │  (streams)  │  │
│                                    └─────────────┘  │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│                   静态外壳                       │
│ (立即发送至浏览器)                       │
│                                                      │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │
│  │   头部组件    │  │  缓存内容    │  │  Suspense   │  │
│  │  (静态)   │  │             │  │  回退组件   │  │
│  └─────────────┘  └─────────────┘  └──────┬──────┘  │
│                                           │         │
│                                    ┌──────▼──────┐  │
│                                    │  动态内容    │  │
│                                    │  (流式传输)  │  │
│                                    └─────────────┘  │
└─────────────────────────────────────────────────────┘

Mental Model: The Caching Decision Tree

思维模型:缓存决策树

When writing a React Server Component, ask these questions in order:
┌─────────────────────────────────────────────────────────┐
│ Does this component fetch data or perform I/O?          │
└─────────────────────┬───────────────────────────────────┘
           ┌──────────▼──────────┐
           │   YES               │ NO → Pure component, no action needed
           └──────────┬──────────┘
    ┌─────────────────▼─────────────────┐
    │ Does it depend on request context? │
    │ (cookies, headers, searchParams)   │
    └─────────────────┬─────────────────┘
         ┌────────────┴────────────┐
         │                         │
    ┌────▼────┐              ┌─────▼─────┐
    │   YES   │              │    NO     │
    └────┬────┘              └─────┬─────┘
         │                         │
         │                   ┌─────▼─────────────────┐
         │                   │ Can this be cached?   │
         │                   │ (same for all users?) │
         │                   └─────┬─────────────────┘
         │                         │
         │              ┌──────────┴──────────┐
         │              │                     │
         │         ┌────▼────┐          ┌─────▼─────┐
         │         │   YES   │          │    NO     │
         │         └────┬────┘          └─────┬─────┘
         │              │                     │
         │              ▼                     │
         │         'use cache'                │
         │         + cacheTag()               │
         │         + cacheLife()              │
         │                                    │
         │    Can you extract runtime         │
         │    data as params?                 │
         │         │                          │
         │    ┌────▼────┐  ┌─────▼─────┐     │
         │    │   YES   │  │    NO     │     │
         │    └────┬────┘  └─────┬─────┘     │
         │         │             │            │
         │    Pass as args  'use cache:      │
         │    to cached fn   private'        │
         │                  (last resort)    │
         │                                    │
         └──────────────┬─────────────────────┘
              Wrap in <Suspense>
              (dynamic streaming)
Key insight: The
'use cache'
directive is for data that's the same across users. User-specific data stays dynamic with Suspense. If you cannot extract runtime data as function parameters and compliance prevents cross-request sharing, use
'use cache: private'
as a last resort.
编写React Server Component时,按顺序思考以下问题:
┌─────────────────────────────────────────────────────────┐
│ 该组件是否需要获取数据或执行I/O操作?          │
└─────────────────────┬───────────────────────────────────┘
           ┌──────────▼──────────┐
           │   是               │ 否 → 纯组件,无需额外操作
           └──────────┬──────────┘
    ┌─────────────────▼─────────────────┐
    │ 它是否依赖请求上下文? │
    │ (cookies、headers、searchParams)   │
    └─────────────────┬─────────────────┘
         ┌────────────┴────────────┐
         │                         │
    ┌────▼────┐              ┌─────▼─────┐
    │   是   │              │    否     │
    └────┬────┘              └─────┬─────┘
         │                         │
         │                   ┌─────▼─────────────────┐
         │                   │ 该数据是否可缓存?   │
         │                   │ (对所有用户都相同?) │
         │                   └─────┬─────────────────┘
         │                         │
         │              ┌──────────┴──────────┐
         │              │                     │
         │         ┌────▼────┐          ┌─────▼─────┐
         │         │   是   │          │    否     │
         │         └────┬────┘          └─────┬─────┘
         │              │                     │
         │              ▼                     │
         │         'use cache'                │
         │         + cacheTag()               │
         │         + cacheLife()              │
         │                                    │
         │    能否将运行时数据提取为参数?         │
         │         │                          │
         │    ┌────▼────┐  ┌─────▼─────┐     │
         │    │   是   │  │    否     │     │
         │    └────┬────┘  └─────┬─────┘     │
         │         │             │            │
         │    作为参数传递给缓存函数  'use cache:      │
         │                     private'        │
         │                  (最后手段)    │
         │                                    │
         └──────────────┬─────────────────────┘
              用<Suspense>包裹
              (动态流式传输)
关键见解
'use cache'
指令适用于所有用户共享相同数据的场景。用户特定数据通过Suspense保持动态。如果无法将运行时数据提取为函数参数,且合规要求禁止跨请求共享数据,则将
'use cache: private'
作为最后手段。

Quick Start

快速开始

Enable Cache Components

启用Cache Components

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

const nextConfig: NextConfig = {
  cacheComponents: true,
}

export default nextConfig
typescript
// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  cacheComponents: true,
}

export default nextConfig

Basic Usage

基础用法

tsx
// Cached component - output included in static shell
async function CachedPosts() {
  'use cache'
  const posts = await db.posts.findMany()
  return <PostList posts={posts} />
}

// Page with static + cached + dynamic content
export default async function BlogPage() {
  return (
    <>
      <Header /> {/* Static */}
      <CachedPosts /> {/* Cached */}
      <Suspense fallback={<Skeleton />}>
        <DynamicComments /> {/* Dynamic - streams */}
      </Suspense>
    </>
  )
}
tsx
// 缓存组件 - 输出内容包含在静态外壳中
async function CachedPosts() {
  'use cache'
  const posts = await db.posts.findMany()
  return <PostList posts={posts} />
}

// 包含静态+缓存+动态内容的页面
export default async function BlogPage() {
  return (
    <>
      <Header /> {/* 静态 */}
      <CachedPosts /> {/* 缓存 */}
      <Suspense fallback={<Skeleton />}>
        <DynamicComments /> {/* 动态 - 流式传输 */}
      </Suspense>
    </>
  )
}

Server Actions vs Data Fetching (Critical Rule)

Server Actions vs 数据获取(关键规则)

Server Actions are for MUTATIONS ONLY - never for data fetching:
PurposeUseExample Functions
Data fetchServer Component /
'use cache'
getProducts()
,
getUser(id)
MutationServer Action (
'use server'
)
createProduct()
,
updateUser()
,
deletePost()
Server Actions仅用于突变操作——绝不能用于数据获取:
用途正确用法示例函数
数据获取Server Component /
'use cache'
getProducts()
,
getUser(id)
突变操作Server Action (
'use server'
)
createProduct()
,
updateUser()
,
deletePost()

❌ WRONG: Server Action for Data Fetching

❌ 错误用法:用Server Action进行数据获取

tsx
"use server"
export async function getProducts() {
  return await db.products.findMany() // NO! This is not a mutation
}

"use server"
export async function getTheme() {
  return (await cookies()).get("theme")?.value // NO! Just reading data
}
tsx
"use server"
export async function getProducts() {
  return await db.products.findMany() // 错误!这不是突变操作
}

"use server"
export async function getTheme() {
  return (await cookies()).get("theme")?.value // 错误!这只是读取数据
}

✅ CORRECT: Data Function + Server Component

✅ 正确用法:数据函数 + Server Component

tsx
// data/products.ts - Cached data function
export async function getProducts() {
  "use cache"
  cacheTag("products")
  cacheLife("hours")
  return await db.products.findMany()
}

// page.tsx - Server Component reads data directly
import { cookies } from "next/headers"

export default async function Page() {
  const products = await getProducts()
  const theme = (await cookies()).get("theme")?.value ?? "light"
  return <ProductList products={products} theme={theme} />
}
tsx
// data/products.ts - 缓存数据函数
export async function getProducts() {
  "use cache"
  cacheTag("products")
  cacheLife("hours")
  return await db.products.findMany()
}

// page.tsx - Server Component直接读取数据
import { cookies } from "next/headers"

export default async function Page() {
  const products = await getProducts()
  const theme = (await cookies()).get("theme")?.value ?? "light"
  return <ProductList products={products} theme={theme} />
}

✅ CORRECT: Server Action for Mutation

✅ 正确用法:用Server Action进行突变操作

tsx
"use server"
import { updateTag } from "next/cache"

export async function createProduct(formData: FormData) {
  await db.products.create({ data: formData })
  updateTag("products") // Invalidate cache after mutation
}
tsx
"use server"
import { updateTag } from "next/cache"

export async function createProduct(formData: FormData) {
  await db.products.create({ data: formData })
  updateTag("products") // 突变后使缓存失效
}

Core APIs

核心API

1.
'use cache'
Directive

1.
'use cache'
指令

Marks code as cacheable. Can be applied at three levels:
tsx
// File-level: All exports are cached
'use cache'
export async function getData() {
  /* ... */
}
export async function Component() {
  /* ... */
}

// Component-level
async function UserCard({ id }: { id: string }) {
  'use cache'
  const user = await fetchUser(id)
  return <Card>{user.name}</Card>
}

// Function-level
async function fetchWithCache(url: string) {
  'use cache'
  return fetch(url).then((r) => r.json())
}
Important: All cached functions must be
async
.
标记代码为可缓存。可在三个级别应用:
tsx
// 文件级别:所有导出内容均被缓存
'use cache'
export async function getData() {
  /* ... */
}
export async function Component() {
  /* ... */
}

// 组件级别
async function UserCard({ id }: { id: string }) {
  'use cache'
  const user = await fetchUser(id)
  return <Card>{user.name}</Card>
}

// 函数级别
async function fetchWithCache(url: string) {
  'use cache'
  return fetch(url).then((r) => r.json())
}
重要提示:所有缓存函数必须是
async
函数。

2.
cacheLife()
- Control Cache Duration

2.
cacheLife()
- 控制缓存时长

tsx
import { cacheLife } from 'next/cache'

async function Posts() {
  'use cache'
  cacheLife('hours') // Use a predefined profile

  // Or custom configuration:
  cacheLife({
    stale: 60, // 1 min - client cache validity
    revalidate: 3600, // 1 hr - start background refresh
    expire: 86400, // 1 day - absolute expiration
  })

  return await db.posts.findMany()
}
Predefined profiles:
'default'
,
'seconds'
,
'minutes'
,
'hours'
,
'days'
,
'weeks'
,
'max'
tsx
import { cacheLife } from 'next/cache'

async function Posts() {
  'use cache'
  cacheLife('hours') // 使用预定义配置文件

  // 或自定义配置:
  cacheLife({
    stale: 60, // 1分钟 - 客户端缓存有效期
    revalidate: 3600, // 1小时 - 启动后台刷新
    expire: 86400, // 1天 - 绝对过期时间
  })

  return await db.posts.findMany()
}
预定义配置文件
'default'
,
'seconds'
,
'minutes'
,
'hours'
,
'days'
,
'weeks'
,
'max'

3.
cacheTag()
- Tag for Invalidation

3.
cacheTag()
- 用于失效的标签

tsx
import { cacheTag } from 'next/cache'

async function BlogPosts() {
  'use cache'
  cacheTag('posts')
  cacheLife('days')

  return await db.posts.findMany()
}

async function UserProfile({ userId }: { userId: string }) {
  'use cache'
  cacheTag('users', `user-${userId}`) // Multiple tags

  return await db.users.findUnique({ where: { id: userId } })
}
tsx
import { cacheTag } from 'next/cache'

async function BlogPosts() {
  'use cache'
  cacheTag('posts')
  cacheLife('days')

  return await db.posts.findMany()
}

async function UserProfile({ userId }: { userId: string }) {
  'use cache'
  cacheTag('users', `user-${userId}`) // 多个标签

  return await db.users.findUnique({ where: { id: userId } })
}

4.
updateTag()
- Immediate Invalidation

4.
updateTag()
- 立即失效

For read-your-own-writes semantics:
tsx
'use server'
import { updateTag } from 'next/cache'

export async function createPost(formData: FormData) {
  await db.posts.create({ data: formData })

  updateTag('posts') // Client immediately sees fresh data
}
用于实现读己所写语义:
tsx
'use server'
import { updateTag } from 'next/cache'

export async function createPost(formData: FormData) {
  await db.posts.create({ data: formData })

  updateTag('posts') // 客户端立即看到最新数据
}

5.
revalidateTag()
- Background Revalidation

5.
revalidateTag()
- 后台重新验证

For stale-while-revalidate pattern:
tsx
'use server'
import { revalidateTag } from 'next/cache'

export async function updatePost(id: string, data: FormData) {
  await db.posts.update({ where: { id }, data })

  revalidateTag('posts', 'max') // Serve stale, refresh in background
}
用于实现stale-while-revalidate模式:
tsx
'use server'
import { revalidateTag } from 'next/cache'

export async function updatePost(id: string, data: FormData) {
  await db.posts.update({ where: { id }, data })

  revalidateTag('posts', 'max') // 提供过期数据,后台刷新
}

When to Use Each Pattern

模式选择指南

Content TypeAPIBehavior
StaticNo directiveRendered at build time
Cached
'use cache'
Included in static shell, revalidates
DynamicInside
<Suspense>
Streams at request time
内容类型API行为
静态无指令构建时渲染
缓存
'use cache'
包含在静态外壳中,可重新验证
动态包裹在
<Suspense>
请求时流式传输

Parameter Permutations & Subshells

参数排列与子外壳

Critical Concept: With Cache Components, Next.js renders ALL permutations of provided parameters to create reusable subshells.
tsx
// app/products/[category]/[slug]/page.tsx
export async function generateStaticParams() {
  return [
    { category: 'jackets', slug: 'classic-bomber' },
    { category: 'jackets', slug: 'essential-windbreaker' },
    { category: 'accessories', slug: 'thermal-fleece-gloves' },
  ]
}
Next.js renders these routes:
/products/jackets/classic-bomber        ← Full params (complete page)
/products/jackets/essential-windbreaker ← Full params (complete page)
/products/accessories/thermal-fleece-gloves ← Full params (complete page)
/products/jackets/[slug]                ← Partial params (category subshell)
/products/accessories/[slug]            ← Partial params (category subshell)
/products/[category]/[slug]             ← No params (fallback shell)
Why this matters: The category subshell (
/products/jackets/[slug]
) can be reused for ANY jacket product, even ones not in
generateStaticParams
. Users navigating to an unlisted jacket get the cached category shell immediately, with product details streaming in.
核心概念:通过Cache Components,Next.js会渲染所有参数排列组合,以创建可复用的子外壳。
tsx
// app/products/[category]/[slug]/page.tsx
export async function generateStaticParams() {
  return [
    { category: 'jackets', slug: 'classic-bomber' },
    { category: 'jackets', slug: 'essential-windbreaker' },
    { category: 'accessories', slug: 'thermal-fleece-gloves' },
  ]
}
Next.js会渲染以下路由:
/products/jackets/classic-bomber        ← 完整参数(完整页面)
/products/jackets/essential-windbreaker ← 完整参数(完整页面)
/products/accessories/thermal-fleece-gloves ← 完整参数(完整页面)
/products/jackets/[slug]                ← 部分参数(分类子外壳)
/products/accessories/[slug]            ← 部分参数(分类子外壳)
/products/[category]/[slug]             ← 无参数(回退外壳)
重要性:分类子外壳(
/products/jackets/[slug]
)可复用至任何夹克产品,即使该产品不在
generateStaticParams
列表中。用户访问未列出的夹克产品时,会立即获取缓存的分类外壳,产品详情则流式传输。

generateStaticParams
Requirements

generateStaticParams
要求

With Cache Components enabled:
  1. Must provide at least one parameter - Empty arrays now cause build errors (prevents silent production failures)
  2. Params prove static safety - Providing params lets Next.js verify no dynamic APIs are called
  3. Partial params create subshells - Each unique permutation generates a reusable shell
tsx
// ❌ ERROR with Cache Components
export function generateStaticParams() {
  return [] // Build error: must provide at least one param
}

// ✅ CORRECT: Provide real params
export async function generateStaticParams() {
  const products = await getPopularProducts()
  return products.map(({ category, slug }) => ({ category, slug }))
}
启用Cache Components后:
  1. 必须提供至少一个参数 - 空数组会导致构建错误(避免静默生产故障)
  2. 参数需证明静态安全性 - 提供参数可让Next.js验证未调用动态API
  3. 部分参数创建子外壳 - 每个唯一的参数排列组合都会生成可复用的外壳
tsx
// ❌ 启用Cache Components时会报错
export function generateStaticParams() {
  return [] // 构建错误:必须提供至少一个参数
}

// ✅ 正确用法:提供真实参数
export async function generateStaticParams() {
  const products = await getPopularProducts()
  return products.map(({ category, slug }) => ({ category, slug }))
}

Cache Key = Arguments

缓存键 = 参数

Arguments become part of the cache key:
tsx
// Different userId = different cache entry
async function UserData({ userId }: { userId: string }) {
  'use cache'
  cacheTag(`user-${userId}`)

  return await fetchUser(userId)
}
参数会成为缓存键的一部分:
tsx
// 不同的userId对应不同的缓存条目
async function UserData({ userId }: { userId: string }) {
  'use cache'
  cacheTag(`user-${userId}`)

  return await fetchUser(userId)
}

Build-Time Feedback

构建时反馈

Cache Components provides early feedback during development. These build errors guide you toward optimal patterns:
Cache Components在开发阶段提供早期反馈。这些构建错误会引导你采用最优模式

Error: Dynamic data outside Suspense

错误:Suspense边界外的动态数据

Error: Accessing cookies/headers/searchParams outside a Suspense boundary
Solution: Wrap dynamic components in
<Suspense>
:
tsx
<Suspense fallback={<Skeleton />}>
  <ComponentThatUsesCookies />
</Suspense>
Error: Accessing cookies/headers/searchParams outside a Suspense boundary
解决方案:用
<Suspense>
包裹动态组件:
tsx
<Suspense fallback={<Skeleton />}>
  <ComponentThatUsesCookies />
</Suspense>

Error: Uncached data outside Suspense

错误:Suspense边界外的未缓存数据

Error: Accessing uncached data outside Suspense
Solution: Either cache the data or wrap in Suspense:
tsx
// Option 1: Cache it
async function ProductData({ id }: { id: string }) {
  'use cache'
  return await db.products.findUnique({ where: { id } })
}

// Option 2: Make it dynamic with Suspense
;<Suspense fallback={<Loading />}>
  <DynamicProductData id={id} />
</Suspense>
Error: Accessing uncached data outside Suspense
解决方案:要么缓存数据,要么用Suspense包裹:
tsx
// 选项1:缓存数据
async function ProductData({ id }: { id: string }) {
  'use cache'
  return await db.products.findUnique({ where: { id } })
}

// 选项2:用Suspense实现动态
;<Suspense fallback={<Loading />}>
  <DynamicProductData id={id} />
</Suspense>

Error: Request data inside cache

错误:缓存内的请求数据

Error: Cannot access cookies/headers inside 'use cache'
Solution: Extract runtime data outside cache boundary (see "Handling Runtime Data" above).
Error: Cannot access cookies/headers inside 'use cache'
解决方案:将运行时数据提取到缓存边界外(见上文“处理运行时数据”)。

Additional Resources

额外资源

  • For complete API reference, see REFERENCE.md
  • For common patterns and recipes, see PATTERNS.md
  • For debugging and troubleshooting, see TROUBLESHOOTING.md
  • 完整API参考,请查看REFERENCE.md
  • 常见模式与实践,请查看PATTERNS.md
  • 调试与故障排除,请查看TROUBLESHOOTING.md

Code Generation Guidelines

代码生成指南

When generating Cache Component code:
  1. Always use
    async
    - All cached functions must be async
  2. Place
    'use cache'
    first
    - Must be first statement in function body
  3. Call
    cacheLife()
    early
    - Should follow
    'use cache'
    directive
  4. Tag meaningfully - Use semantic tags that match your invalidation needs
  5. Extract runtime data - Move
    cookies()
    /
    headers()
    outside cached scope
  6. Wrap dynamic content - Use
    <Suspense>
    for non-cached async components
  7. Use
    'use cache: private'
    as last resort
    - Only when runtime data cannot be extracted as params AND compliance requires no cross-request sharing

生成Cache Component代码时:
  1. 始终使用
    async
    - 所有缓存函数必须是异步的
  2. 'use cache'
    放在首位
    - 必须是函数体的第一条语句
  3. 尽早调用
    cacheLife()
    - 应紧跟
    'use cache'
    指令
  4. 有意义地标记 - 使用与失效需求匹配的语义化标签
  5. 提取运行时数据 - 将
    cookies()
    /
    headers()
    移到缓存作用域外
  6. 包裹动态内容 - 用
    <Suspense>
    包裹非缓存异步组件
  7. 仅在最后手段时使用
    'use cache: private'
    - 仅当无法将运行时数据提取为参数且合规要求禁止跨请求共享时使用

Proactive Application (When Cache Components Enabled)

主动应用(当Cache Components启用时)

When
cacheComponents: true
is detected in the project, automatically apply these patterns:
当在项目中检测到
cacheComponents: true
时,自动应用以下模式

When Writing Data Fetching Components

编写数据获取组件时

Ask yourself: "Can this data be cached?" If yes, add
'use cache'
:
tsx
// Before: Uncached fetch
async function ProductList() {
  const products = await db.products.findMany()
  return <Grid products={products} />
}

// After: With caching
async function ProductList() {
  'use cache'
  cacheTag('products')
  cacheLife('hours')

  const products = await db.products.findMany()
  return <Grid products={products} />
}
自问:“此数据是否可缓存?”如果是,添加
'use cache'
tsx
// 之前:未缓存的获取
async function ProductList() {
  const products = await db.products.findMany()
  return <Grid products={products} />
}

// 之后:添加缓存
async function ProductList() {
  'use cache'
  cacheTag('products')
  cacheLife('hours')

  const products = await db.products.findMany()
  return <Grid products={products} />
}

When Writing Server Actions

编写Server Actions时

Always invalidate relevant caches after mutations:
tsx
'use server'
import { updateTag } from 'next/cache'

export async function createProduct(data: FormData) {
  await db.products.create({ data })
  updateTag('products') // Don't forget!
}
突变操作后始终使相关缓存失效:
tsx
'use server'
import { updateTag } from 'next/cache'

export async function createProduct(data: FormData) {
  await db.products.create({ data })
  updateTag('products') // 不要忘记!
}

When Composing Pages

组合页面时

Structure with static shell + cached content + dynamic streaming:
tsx
export default async function Page() {
  return (
    <>
      <StaticHeader /> {/* No cache needed */}
      <CachedContent /> {/* 'use cache' */}
      <Suspense fallback={<Skeleton />}>
        <DynamicUserContent /> {/* Streams at runtime */}
      </Suspense>
    </>
  )
}
按静态外壳 + 缓存内容 + 动态流的结构组织:
tsx
export default async function Page() {
  return (
    <>
      <StaticHeader /> {/* 无需缓存 */}
      <CachedContent /> {/* 使用'use cache' */}
      <Suspense fallback={<Skeleton />}>
        <DynamicUserContent /> {/* 运行时流式传输 */}
      </Suspense>
    </>
  )
}

When Reviewing Code

评审代码时

Flag these issues in Cache Components projects:
  • Data fetching without
    'use cache'
    where caching would benefit
  • Missing
    cacheTag()
    calls (makes invalidation impossible)
  • Missing
    cacheLife()
    (relies on defaults which may not be appropriate)
  • Server Actions without
    updateTag()
    /
    revalidateTag()
    after mutations
  • cookies()
    /
    headers()
    called inside
    'use cache'
    scope
  • Dynamic components without
    <Suspense>
    boundaries
  • DEPRECATED:
    export const revalidate
    - replace with
    cacheLife()
    in
    'use cache'
  • DEPRECATED:
    export const dynamic
    - replace with Suspense + cache boundaries
  • Empty
    generateStaticParams()
    return - must provide at least one param
在Cache Components项目中标记以下问题:
  • 可从缓存受益的数据获取逻辑未使用
    'use cache'
  • 缺少
    cacheTag()
    调用(使缓存失效无法实现)
  • 缺少
    cacheLife()
    (依赖默认配置,可能不合适)
  • 突变操作后的Server Actions未调用
    updateTag()
    /
    revalidateTag()
  • 'use cache'
    作用域内调用
    cookies()
    /
    headers()
  • 动态组件未用
    <Suspense>
    边界包裹
  • 已废弃
    export const revalidate
    - 替换为
    'use cache'
    作用域内的
    cacheLife()
  • 已废弃
    export const dynamic
    - 替换为Suspense + 缓存边界
  • generateStaticParams()
    返回空数组 - 必须提供至少一个参数