revalidation-strategy-planner

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Revalidation Strategy Planner

重新验证策略规划器

Analyze Next.js application routes and recommend optimal caching and revalidation strategies for performance and data freshness.
分析Next.js应用路由,为性能与数据新鲜度推荐最优的缓存和重新验证策略。

Overview

概述

To optimize Next.js caching strategies:
  1. Analyze route characteristics (data freshness requirements, update frequency)
  2. Determine appropriate rendering strategy (SSG, ISR, SSR, streaming)
  3. Configure revalidation intervals for ISR routes
  4. Implement cache tags for on-demand revalidation
  5. Set up streaming for progressive page loading
要优化Next.js缓存策略:
  1. 分析路由特性(数据新鲜度要求、更新频率)
  2. 确定合适的渲染策略(SSG、ISR、SSR、流式传输)
  3. 为ISR路由配置重新验证间隔
  4. 实现基于缓存标签的按需重新验证
  5. 设置流式传输以实现渐进式页面加载

Rendering Strategies

渲染策略

Static Site Generation (SSG)

静态站点生成(SSG)

To use SSG for rarely changing content:
typescript
// app/about/page.tsx
export default async function AboutPage() {
  // Generated at build time, no revalidation
  return <div>About Us</div>;
}
Best for:
  • Marketing pages
  • Documentation
  • Static content that rarely changes
针对极少变更的内容使用SSG:
typescript
// app/about/page.tsx
export default async function AboutPage() {
  // Generated at build time, no revalidation
  return <div>About Us</div>;
}
最适合:
  • 营销页面
  • 文档
  • 极少变更的静态内容

Incremental Static Regeneration (ISR)

增量静态再生(ISR)

To use ISR for periodically updated content:
typescript
// app/entities/[id]/page.tsx
export const revalidate = 3600; // Revalidate every hour

export default async function EntityPage({ params }: { params: { id: string } }) {
  const entity = await fetchEntity(params.id);
  return <EntityDetail entity={entity} />;
}
Best for:
  • Entity detail pages
  • Blog posts
  • Product listings
  • Content with predictable update patterns
针对定期更新的内容使用ISR:
typescript
// app/entities/[id]/page.tsx
export const revalidate = 3600; // Revalidate every hour

export default async function EntityPage({ params }: { params: { id: string } }) {
  const entity = await fetchEntity(params.id);
  return <EntityDetail entity={entity} />;
}
最适合:
  • 实体详情页
  • 博客文章
  • 产品列表
  • 有可预测更新模式的内容

Server-Side Rendering (SSR)

服务器端渲染(SSR)

To use SSR for real-time data:
typescript
// app/dashboard/page.tsx
export const dynamic = 'force-dynamic';

export default async function Dashboard() {
  const data = await fetchUserData();
  return <DashboardView data={data} />;
}
Best for:
  • User dashboards
  • Personalized content
  • Real-time data displays
  • Authentication-dependent pages
针对实时数据使用SSR:
typescript
// app/dashboard/page.tsx
export const dynamic = 'force-dynamic';

export default async function Dashboard() {
  const data = await fetchUserData();
  return <DashboardView data={data} />;
}
最适合:
  • 用户仪表盘
  • 个性化内容
  • 实时数据展示
  • 依赖认证的页面

Streaming

流式传输

To use streaming for progressive loading:
typescript
// app/timeline/page.tsx
import { Suspense } from 'react';

export default function TimelinePage() {
  return (
    <div>
      <TimelineHeader />
      <Suspense fallback={<TimelineLoader />}>
        <TimelineEvents />
      </Suspense>
    </div>
  );
}
Best for:
  • Pages with slow data fetching
  • Complex pages with multiple data sources
  • Improving perceived performance
Consult
references/rendering-strategies.md
for detailed strategy comparison.
针对渐进式加载使用流式传输:
typescript
// app/timeline/page.tsx
import { Suspense } from 'react';

export default function TimelinePage() {
  return (
    <div>
      <TimelineHeader />
      <Suspense fallback={<TimelineLoader />}>
        <TimelineEvents />
      </Suspense>
    </div>
  );
}
最适合:
  • 数据获取缓慢的页面
  • 包含多数据源的复杂页面
  • 提升感知性能
如需详细的策略对比,请参考
references/rendering-strategies.md

Revalidation Configuration

重新验证配置

Time-Based Revalidation

基于时间的重新验证

To set revalidation intervals:
typescript
// Revalidate every 60 seconds
export const revalidate = 60;

// Revalidate every hour
export const revalidate = 3600;

// Revalidate every day
export const revalidate = 86400;
设置重新验证间隔:
typescript
// 每60秒重新验证一次
export const revalidate = 60;

// 每小时重新验证一次
export const revalidate = 3600;

// 每天重新验证一次
export const revalidate = 86400;

On-Demand Revalidation

按需重新验证

To implement on-demand cache invalidation:
typescript
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';
import { NextRequest } from 'next/server';

export async function POST(request: NextRequest) {
  const { path, tag } = await request.json();

  if (path) {
    revalidatePath(path);
  }

  if (tag) {
    revalidateTag(tag);
  }

  return Response.json({ revalidated: true, now: Date.now() });
}
Use from Server Actions:
typescript
'use server';

import { revalidatePath } from 'next/cache';

export async function updateEntity(id: string, data: EntityData) {
  await saveEntity(id, data);
  revalidatePath(`/entities/${id}`);
  revalidatePath('/entities');
}
实现按需缓存失效:
typescript
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';
import { NextRequest } from 'next/server';

export async function POST(request: NextRequest) {
  const { path, tag } = await request.json();

  if (path) {
    revalidatePath(path);
  }

  if (tag) {
    revalidateTag(tag);
  }

  return Response.json({ revalidated: true, now: Date.now() });
}
从Server Actions中调用:
typescript
'use server';

import { revalidatePath } from 'next/cache';

export async function updateEntity(id: string, data: EntityData) {
  await saveEntity(id, data);
  revalidatePath(`/entities/${id}`);
  revalidatePath('/entities');
}

Cache Tags

缓存标签

To implement cache tag-based revalidation:
typescript
// app/entities/[id]/page.tsx
export default async function EntityPage({ params }: { params: { id: string } }) {
  const entity = await fetch(`/api/entities/${params.id}`, {
    next: {
      tags: [`entity-${params.id}`, 'entities'],
    },
  });

  return <EntityDetail entity={entity} />;
}
Revalidate by tag:
typescript
import { revalidateTag } from 'next/cache';

// Revalidate all pages with 'entities' tag
revalidateTag('entities');

// Revalidate specific entity
revalidateTag(`entity-${entityId}`);
Reference
assets/cache-tag-patterns.ts
for cache tagging patterns.
实现基于缓存标签的重新验证:
typescript
// app/entities/[id]/page.tsx
export default async function EntityPage({ params }: { params: { id: string } }) {
  const entity = await fetch(`/api/entities/${params.id}`, {
    next: {
      tags: [`entity-${params.id}`, 'entities'],
    },
  });

  return <EntityDetail entity={entity} />;
}
按标签重新验证:
typescript
import { revalidateTag } from 'next/cache';

// 重新验证所有带有'entities'标签的页面
revalidateTag('entities');

// 重新验证特定实体
revalidateTag(`entity-${entityId}`);
如需缓存标签模式参考,请查看
assets/cache-tag-patterns.ts

Route Analysis

路由分析

Use
scripts/analyze_routes.py
to analyze application routes and recommend strategies:
bash
python scripts/analyze_routes.py ./app
Output includes:
  • Route path
  • Recommended rendering strategy
  • Suggested revalidation interval
  • Appropriate cache tags
  • Reasoning for recommendations
使用
scripts/analyze_routes.py
分析应用路由并推荐策略:
bash
python scripts/analyze_routes.py ./app
输出内容包括:
  • 路由路径
  • 推荐的渲染策略
  • 建议的重新验证间隔
  • 适用的缓存标签
  • 推荐理由

Analysis Criteria

分析标准

Consider these factors:
  1. Data Freshness Requirements
    • Real-time: SSR or very short revalidation (1-60s)
    • Near real-time: ISR with short interval (60-300s)
    • Periodic updates: ISR with medium interval (300-3600s)
    • Rarely changes: SSG or long interval (3600s+)
  2. Update Frequency
    • Continuous: SSR
    • Multiple times per hour: ISR (60-300s)
    • Hourly: ISR (3600s)
    • Daily: ISR (86400s)
    • Weekly+: SSG
  3. Personalization
    • User-specific: SSR
    • Role-based: SSR or ISR with user context
    • Public: SSG or ISR
  4. Data Source Performance
    • Fast (<100ms): Any strategy
    • Medium (100-500ms): Consider streaming
    • Slow (>500ms): Use streaming or aggressive caching
Consult
references/decision-matrix.md
for the complete decision matrix.
需考虑以下因素:
  1. 数据新鲜度要求
    • 实时:SSR或极短的重新验证间隔(1-60秒)
    • 近实时:ISR搭配短间隔(60-300秒)
    • 定期更新:ISR搭配中等间隔(300-3600秒)
    • 极少变更:SSG或长间隔(3600秒以上)
  2. 更新频率
    • 持续更新:SSR
    • 每小时多次:ISR(60-300秒)
    • 每小时一次:ISR(3600秒)
    • 每天一次:ISR(86400秒)
    • 每周及以上:SSG
  3. 个性化程度
    • 用户专属:SSR
    • 基于角色:SSR或带用户上下文的ISR
    • 公开内容:SSG或ISR
  4. 数据源性能
    • 快速(<100ms):任意策略
    • 中等(100-500ms):考虑使用流式传输
    • 缓慢(>500ms):使用流式传输或激进缓存
如需完整的决策矩阵,请参考
references/decision-matrix.md

Implementation Patterns

实现模式

Entity Detail Pages

实体详情页

To optimize entity pages:
typescript
// app/entities/[id]/page.tsx
export const revalidate = 1800; // 30 minutes

export async function generateStaticParams() {
  const entities = await fetchAllEntityIds();
  return entities.map((id) => ({ id: id.toString() }));
}

export default async function EntityPage({ params }: { params: { id: string } }) {
  const entity = await fetchEntity(params.id, {
    next: { tags: [`entity-${params.id}`, 'entities'] },
  });

  return <EntityDetail entity={entity} />;
}
优化实体页面:
typescript
// app/entities/[id]/page.tsx
export const revalidate = 1800; // 30分钟
export async function generateStaticParams() {
  const entities = await fetchAllEntityIds();
  return entities.map((id) => ({ id: id.toString() }));
}

export default async function EntityPage({ params }: { params: { id: string } }) {
  const entity = await fetchEntity(params.id, {
    next: { tags: [`entity-${params.id}`, 'entities'] },
  });

  return <EntityDetail entity={entity} />;
}

List Pages

列表页

To optimize listing pages:
typescript
// app/entities/page.tsx
export const revalidate = 300; // 5 minutes

export default async function EntitiesPage({
  searchParams,
}: {
  searchParams: { page?: string };
}) {
  const page = parseInt(searchParams.page || '1');
  const entities = await fetchEntities(page, {
    next: { tags: ['entities'] },
  });

  return <EntityList entities={entities} />;
}
优化列表页面:
typescript
// app/entities/page.tsx
export const revalidate = 300; // 5分钟

export default async function EntitiesPage({
  searchParams,
}: {
  searchParams: { page?: string };
}) {
  const page = parseInt(searchParams.page || '1');
  const entities = await fetchEntities(page, {
    next: { tags: ['entities'] },
  });

  return <EntityList entities={entities} />;
}

Timeline Pages

时间线页面

To optimize timeline with streaming:
typescript
// app/timeline/page.tsx
import { Suspense } from 'react';

export default function TimelinePage() {
  return (
    <div>
      <Suspense fallback={<TimelineHeaderSkeleton />}>
        <TimelineHeader />
      </Suspense>
      <Suspense fallback={<EventsSkeleton />}>
        <TimelineEvents />
      </Suspense>
    </div>
  );
}

async function TimelineEvents() {
  const events = await fetchTimelineEvents({
    next: { tags: ['timeline'], revalidate: 600 },
  });
  return <EventsList events={events} />;
}
使用流式传输优化时间线:
typescript
// app/timeline/page.tsx
import { Suspense } from 'react';

export default function TimelinePage() {
  return (
    <div>
      <Suspense fallback={<TimelineHeaderSkeleton />}>
        <TimelineHeader />
      </Suspense>
      <Suspense fallback={<EventsSkeleton />}>
        <TimelineEvents />
      </Suspense>
    </div>
  );
}

async function TimelineEvents() {
  const events = await fetchTimelineEvents({
    next: { tags: ['timeline'], revalidate: 600 },
  });
  return <EventsList events={events} />;
}

Dashboard Pages

仪表盘页面

To implement personalized dashboard:
typescript
// app/dashboard/page.tsx
export const dynamic = 'force-dynamic';

export default async function DashboardPage() {
  const session = await getSession();
  const data = await fetchUserDashboard(session.userId);

  return (
    <div>
      <Suspense fallback={<StatsSkeleton />}>
        <DashboardStats userId={session.userId} />
      </Suspense>
      <Suspense fallback={<ActivitySkeleton />}>
        <RecentActivity userId={session.userId} />
      </Suspense>
    </div>
  );
}
实现个性化仪表盘:
typescript
// app/dashboard/page.tsx
export const dynamic = 'force-dynamic';

export default async function DashboardPage() {
  const session = await getSession();
  const data = await fetchUserDashboard(session.userId);

  return (
    <div>
      <Suspense fallback={<StatsSkeleton />}>
        <DashboardStats userId={session.userId} />
      </Suspense>
      <Suspense fallback={<ActivitySkeleton />}>
        <RecentActivity userId={session.userId} />
      </Suspense>
    </div>
  );
}

Cache Invalidation Strategies

缓存失效策略

Granular Invalidation

细粒度失效

To invalidate specific resources:
typescript
// After entity update
revalidateTag(`entity-${entityId}`);

// After relationship change
revalidateTag(`entity-${sourceId}`);
revalidateTag(`entity-${targetId}`);
revalidateTag('relationships');
失效特定资源:
typescript
// 实体更新后
revalidateTag(`entity-${entityId}`);

// 关联关系变更后
revalidateTag(`entity-${sourceId}`);
revalidateTag(`entity-${targetId}`);
revalidateTag('relationships');

Cascade Invalidation

级联失效

To invalidate related resources:
typescript
async function updateEntity(id: string, data: EntityData) {
  await saveEntity(id, data);

  // Invalidate entity page
  revalidateTag(`entity-${id}`);

  // Invalidate list pages
  revalidateTag('entities');

  // Invalidate related pages
  const relationships = await getEntityRelationships(id);
  for (const rel of relationships) {
    revalidateTag(`entity-${rel.targetId}`);
  }
}
失效关联资源:
typescript
async function updateEntity(id: string, data: EntityData) {
  await saveEntity(id, data);

  // 失效实体页面
  revalidateTag(`entity-${id}`);

  // 失效列表页面
  revalidateTag('entities');

  // 失效关联页面
  const relationships = await getEntityRelationships(id);
  for (const rel of relationships) {
    revalidateTag(`entity-${rel.targetId}`);
  }
}

Batch Invalidation

批量失效

To invalidate multiple resources efficiently:
typescript
async function bulkUpdateEntities(updates: EntityUpdate[]) {
  await saveBulkUpdates(updates);

  // Collect unique tags
  const tags = new Set<string>(['entities']);
  for (const update of updates) {
    tags.add(`entity-${update.id}`);
  }

  // Revalidate all at once
  for (const tag of tags) {
    revalidateTag(tag);
  }
}
高效失效多个资源:
typescript
async function bulkUpdateEntities(updates: EntityUpdate[]) {
  await saveBulkUpdates(updates);

  // 收集唯一标签
  const tags = new Set<string>(['entities']);
  for (const update of updates) {
    tags.add(`entity-${update.id}`);
  }

  // 一次性重新验证所有标签
  for (const tag of tags) {
    revalidateTag(tag);
  }
}

Performance Optimization

性能优化

Stale-While-Revalidate

失效时提供旧内容(Stale-While-Revalidate)

To implement SWR pattern:
typescript
export const revalidate = 60; // Revalidate every minute
export const dynamic = 'force-static'; // Serve stale while revalidating
实现SWR模式:
typescript
export const revalidate = 60; // 每分钟重新验证一次
export const dynamic = 'force-static'; // 重新验证期间提供旧内容

Parallel Data Fetching

并行数据获取

To fetch data in parallel:
typescript
export default async function EntityPage({ params }: { params: { id: string } }) {
  const [entity, relationships, timeline] = await Promise.all([
    fetchEntity(params.id),
    fetchRelationships(params.id),
    fetchTimeline(params.id),
  ]);

  return <EntityDetailView entity={entity} relationships={relationships} timeline={timeline} />;
}
并行获取数据:
typescript
export default async function EntityPage({ params }: { params: { id: string } }) {
  const [entity, relationships, timeline] = await Promise.all([
    fetchEntity(params.id),
    fetchRelationships(params.id),
    fetchTimeline(params.id),
  ]);

  return <EntityDetailView entity={entity} relationships={relationships} timeline={timeline} />;
}

Selective Streaming

选择性流式传输

To stream only slow components:
typescript
export default function EntityPage({ params }: { params: { id: string } }) {
  return (
    <div>
      <EntityHeader id={params.id} /> {/* Fast, no streaming */}
      <Suspense fallback={<RelationshipsSkeleton />}>
        <EntityRelationships id={params.id} /> {/* Slow, stream it */}
      </Suspense>
    </div>
  );
}
仅对缓慢组件启用流式传输:
typescript
export default function EntityPage({ params }: { params: { id: string } }) {
  return (
    <div>
      <EntityHeader id={params.id} /> {/* 加载快,不使用流式传输 */}
      <Suspense fallback={<RelationshipsSkeleton />}>
        <EntityRelationships id={params.id} /> {/* 加载慢,启用流式传输 */}
      </Suspense>
    </div>
  );
}

Monitoring and Testing

监控与测试

To monitor cache performance:
  1. Cache Hit Rates: Track ISR cache hits vs. regenerations
  2. Revalidation Frequency: Monitor how often pages regenerate
  3. Response Times: Measure time to first byte (TTFB)
  4. Stale Serving: Track stale-while-revalidate occurrences
Use Next.js analytics or custom logging:
typescript
// middleware.ts
export function middleware(request: NextRequest) {
  const start = Date.now();

  return NextResponse.next({
    headers: {
      'x-response-time': `${Date.now() - start}ms`,
    },
  });
}
监控缓存性能:
  1. 缓存命中率:跟踪ISR缓存命中与重新生成的次数
  2. 重新验证频率:监控页面重新生成的频率
  3. 响应时间:测量首字节时间(TTFB)
  4. 旧内容服务:跟踪失效时提供旧内容的发生情况
可使用Next.js分析工具或自定义日志:
typescript
// middleware.ts
export function middleware(request: NextRequest) {
  const start = Date.now();

  return NextResponse.next({
    headers: {
      'x-response-time': `${Date.now() - start}ms`,
    },
  });
}

Best Practices

最佳实践

  1. Start Conservative: Begin with shorter revalidation intervals, increase gradually
  2. Use Cache Tags: Prefer tag-based invalidation over path-based
  3. Monitor Performance: Track cache hit rates and response times
  4. Plan Invalidation: Design invalidation strategy with data mutations
  5. Test Edge Cases: Verify behavior with stale data and revalidation
  6. Document Decisions: Record why specific intervals were chosen
  7. Consider Users: Balance freshness with performance
  1. 保守起步:先使用较短的重新验证间隔,再逐步延长
  2. 使用缓存标签:优先选择基于标签的失效而非基于路径的失效
  3. 监控性能:跟踪缓存命中率与响应时间
  4. 规划失效逻辑:结合数据变更设计失效策略
  5. 测试边缘场景:验证旧数据与重新验证的行为
  6. 记录决策:记录选择特定间隔的原因
  7. 以用户为中心:在数据新鲜度与性能之间取得平衡

Troubleshooting

故障排查

Common issues:
  • Stale Data Persisting: Check cache tag implementation and invalidation logic
  • Excessive Regeneration: Increase revalidation interval or fix trigger-happy invalidation
  • Slow Page Loads: Add streaming for slow components
  • Cache Not Working: Verify fetch options and dynamic/static configuration
  • Development vs Production: Remember ISR only works in production builds
常见问题:
  • 旧数据持续存在:检查缓存标签实现与失效逻辑
  • 过度重新生成:延长重新验证间隔或修复过于频繁的失效触发
  • 页面加载缓慢:为缓慢组件添加流式传输
  • 缓存不生效:验证fetch选项与动态/静态配置
  • 开发与生产差异:注意ISR仅在生产构建中生效