revalidation-strategy-planner
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRevalidation 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:
- Analyze route characteristics (data freshness requirements, update frequency)
- Determine appropriate rendering strategy (SSG, ISR, SSR, streaming)
- Configure revalidation intervals for ISR routes
- Implement cache tags for on-demand revalidation
- Set up streaming for progressive page loading
要优化Next.js缓存策略:
- 分析路由特性(数据新鲜度要求、更新频率)
- 确定合适的渲染策略(SSG、ISR、SSR、流式传输)
- 为ISR路由配置重新验证间隔
- 实现基于缓存标签的按需重新验证
- 设置流式传输以实现渐进式页面加载
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 for detailed strategy comparison.
references/rendering-strategies.md针对渐进式加载使用流式传输:
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.mdRevalidation 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 for cache tagging patterns.
assets/cache-tag-patterns.ts实现基于缓存标签的重新验证:
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.tsRoute Analysis
路由分析
Use to analyze application routes and recommend strategies:
scripts/analyze_routes.pybash
python scripts/analyze_routes.py ./appOutput includes:
- Route path
- Recommended rendering strategy
- Suggested revalidation interval
- Appropriate cache tags
- Reasoning for recommendations
使用分析应用路由并推荐策略:
scripts/analyze_routes.pybash
python scripts/analyze_routes.py ./app输出内容包括:
- 路由路径
- 推荐的渲染策略
- 建议的重新验证间隔
- 适用的缓存标签
- 推荐理由
Analysis Criteria
分析标准
Consider these factors:
-
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+)
-
Update Frequency
- Continuous: SSR
- Multiple times per hour: ISR (60-300s)
- Hourly: ISR (3600s)
- Daily: ISR (86400s)
- Weekly+: SSG
-
Personalization
- User-specific: SSR
- Role-based: SSR or ISR with user context
- Public: SSG or ISR
-
Data Source Performance
- Fast (<100ms): Any strategy
- Medium (100-500ms): Consider streaming
- Slow (>500ms): Use streaming or aggressive caching
Consult for the complete decision matrix.
references/decision-matrix.md需考虑以下因素:
-
数据新鲜度要求
- 实时:SSR或极短的重新验证间隔(1-60秒)
- 近实时:ISR搭配短间隔(60-300秒)
- 定期更新:ISR搭配中等间隔(300-3600秒)
- 极少变更:SSG或长间隔(3600秒以上)
-
更新频率
- 持续更新:SSR
- 每小时多次:ISR(60-300秒)
- 每小时一次:ISR(3600秒)
- 每天一次:ISR(86400秒)
- 每周及以上:SSG
-
个性化程度
- 用户专属:SSR
- 基于角色:SSR或带用户上下文的ISR
- 公开内容:SSG或ISR
-
数据源性能
- 快速(<100ms):任意策略
- 中等(100-500ms):考虑使用流式传输
- 缓慢(>500ms):使用流式传输或激进缓存
如需完整的决策矩阵,请参考。
references/decision-matrix.mdImplementation 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:
- Cache Hit Rates: Track ISR cache hits vs. regenerations
- Revalidation Frequency: Monitor how often pages regenerate
- Response Times: Measure time to first byte (TTFB)
- 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`,
},
});
}监控缓存性能:
- 缓存命中率:跟踪ISR缓存命中与重新生成的次数
- 重新验证频率:监控页面重新生成的频率
- 响应时间:测量首字节时间(TTFB)
- 旧内容服务:跟踪失效时提供旧内容的发生情况
可使用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
最佳实践
- Start Conservative: Begin with shorter revalidation intervals, increase gradually
- Use Cache Tags: Prefer tag-based invalidation over path-based
- Monitor Performance: Track cache hit rates and response times
- Plan Invalidation: Design invalidation strategy with data mutations
- Test Edge Cases: Verify behavior with stale data and revalidation
- Document Decisions: Record why specific intervals were chosen
- Consider Users: Balance freshness with performance
- 保守起步:先使用较短的重新验证间隔,再逐步延长
- 使用缓存标签:优先选择基于标签的失效而非基于路径的失效
- 监控性能:跟踪缓存命中率与响应时间
- 规划失效逻辑:结合数据变更设计失效策略
- 测试边缘场景:验证旧数据与重新验证的行为
- 记录决策:记录选择特定间隔的原因
- 以用户为中心:在数据新鲜度与性能之间取得平衡
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仅在生产构建中生效