Next.js 面试题详细答案 - Q9
Q9: Next.js 15 对 fetch 的扩展:解释 fetch 的缓存策略(cache: 'force-cache', 'no-cache', 'no-store')和重新验证策略(revalidate: 3600, next.revalidate)。
Next.js fetch 缓存策略
1. cache: 'force-cache' - 强制缓存
// 永久缓存,直到手动清除
async function StaticData() {
const response = await fetch('https://api.example.com/static-data', {
cache: 'force-cache',
})
const data = await response.json()
return <div>{data}</div>
}
// 适用场景:静态配置、不经常变化的数据
async function SiteConfig() {
const config = await fetch('https://api.example.com/config', {
cache: 'force-cache',
})
return (
<div>
<h1>{config.siteName}</h1>
<p>{config.description}</p>
</div>
)
}
2. cache: 'no-cache' - 不缓存
// 每次都重新验证,但可能使用缓存
async function DynamicData() {
const response = await fetch('https://api.example.com/dynamic-data', {
cache: 'no-cache',
})
const data = await response.json()
return <div>{data}</div>
}
// 适用场景:需要实时验证的数据
async function UserProfile({ userId }) {
const user = await fetch(`https://api.example.com/users/${userId}`, {
cache: 'no-cache',
})
return <div>{user.name}</div>
}
3. cache: 'no-store' - 不存储
// 完全不缓存,每次都重新请求
async function RealTimeData() {
const response = await fetch('https://api.example.com/realtime', {
cache: 'no-store',
})
const data = await response.json()
return <div>{data}</div>
}
// 适用场景:实时数据、个性化内容
async function PersonalizedContent({ userId }) {
const content = await fetch(`https://api.example.com/personalized/${userId}`, {
cache: 'no-store',
})
return <div>{content}</div>
}
重新验证策略
1. revalidate - 时间基础重新验证
// 每 60 秒重新验证
async function ISRData() {
const response = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 },
})
const posts = await response.json()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
// 不同时间间隔的重新验证
async function BlogPost({ slug }) {
const post = await fetch(`https://api.example.com/posts/${slug}`, {
next: { revalidate: 3600 }, // 1小时
})
return <article>{post.content}</article>
}
async function ProductList() {
const products = await fetch('https://api.example.com/products', {
next: { revalidate: 86400 }, // 24小时
})
return <div>{products}</div>
}
2. tags - 标签基础重新验证
// 使用标签进行缓存管理
async function TaggedData() {
const response = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] },
})
const posts = await response.json()
return <div>{posts}</div>
}
// 多个标签
async function ComplexData() {
const response = await fetch('https://api.example.com/complex-data', {
next: { tags: ['posts', 'users', 'analytics'] },
})
return <div>{response}</div>
}
// 按需重新验证 API
// app/api/revalidate/route.js
export async function POST(request) {
const { tags } = await request.json()
try {
await revalidateTag(tags)
return Response.json({ revalidated: true })
} catch (err) {
return Response.json({ error: 'Failed to revalidate' }, { status: 500 })
}
}
3. 组合使用缓存和重新验证
// 时间 + 标签重新验证
async function HybridData() {
const response = await fetch('https://api.example.com/hybrid-data', {
next: {
revalidate: 3600, // 1小时
tags: ['hybrid']
},
})
return <div>{response}</div>
}
// 强制缓存 + 标签
async function StaticWithTags() {
const response = await fetch('https://api.example.com/static-with-tags', {
cache: 'force-cache',
next: { tags: ['static'] },
})
return <div>{response}</div>
}
实际应用示例
1. 博客系统缓存策略
// 首页 - 短期缓存
async function HomePage() {
const featuredPosts = await fetch('https://api.example.com/posts/featured', {
next: { revalidate: 300 }, // 5分钟
})
return <FeaturedPosts posts={featuredPosts} />
}
// 文章列表 - 中期缓存
async function BlogList() {
const posts = await fetch('https://api.example.com/posts', {
next: {
revalidate: 3600, // 1小时
tags: ['posts']
},
})
return <PostList posts={posts} />
}
// 单篇文章 - 长期缓存
async function BlogPost({ slug }) {
const post = await fetch(`https://api.example.com/posts/${slug}`, {
next: {
revalidate: 86400, // 24小时
tags: ['posts', `post-${slug}`]
},
})
return <Article post={post} />
}
// 用户相关 - 不缓存
async function UserDashboard({ userId }) {
const userData = await fetch(`https://api.example.com/users/${userId}/dashboard`, {
cache: 'no-store',
})
return <Dashboard data={userData} />
}
2. 电商系统缓存策略
// 产品列表 - ISR
async function ProductList() {
const products = await fetch('https://api.example.com/products', {
next: {
revalidate: 1800, // 30分钟
tags: ['products']
},
})
return <ProductGrid products={products} />
}
// 产品详情 - ISR + 标签
async function ProductDetail({ id }) {
const product = await fetch(`https://api.example.com/products/${id}`, {
next: {
revalidate: 3600, // 1小时
tags: ['products', `product-${id}`]
},
})
return <ProductInfo product={product} />
}
// 购物车 - 不缓存
async function ShoppingCart({ userId }) {
const cart = await fetch(`https://api.example.com/cart/${userId}`, {
cache: 'no-store',
})
return <Cart items={cart} />
}
// 库存信息 - 短期缓存
async function StockInfo({ productId }) {
const stock = await fetch(`https://api.example.com/stock/${productId}`, {
next: { revalidate: 60 }, // 1分钟
})
return <StockDisplay stock={stock} />
}
缓存管理 API
1. revalidatePath - 路径重新验证
// app/api/posts/route.js
export async function POST(request) {
const newPost = await request.json()
// 创建新文章
const post = await createPost(newPost)
// 重新验证相关路径
revalidatePath('/blog')
revalidatePath(`/blog/${post.slug}`)
return Response.json(post)
}
// 重新验证多个路径
export async function PUT(request) {
const { id, ...updates } = await request.json()
const post = await updatePost(id, updates)
// 重新验证所有相关路径
revalidatePath('/blog')
revalidatePath('/blog/[slug]', 'page')
revalidatePath(`/blog/${post.slug}`)
return Response.json(post)
}
2. revalidateTag - 标签重新验证
// app/api/revalidate/route.js
export async function POST(request) {
const { tag } = await request.json()
try {
await revalidateTag(tag)
return Response.json({ revalidated: true, tag })
} catch (err) {
return Response.json({ error: 'Failed to revalidate' }, { status: 500 })
}
}
// 重新验证多个标签
export async function PUT(request) {
const { tags } = await request.json()
try {
for (const tag of tags) {
await revalidateTag(tag)
}
return Response.json({ revalidated: true, tags })
} catch (err) {
return Response.json({ error: 'Failed to revalidate' }, { status: 500 })
}
}
缓存策略选择指南
1. 数据更新频率
// 静态数据 - 永久缓存
const staticConfig = await fetch('/api/config', {
cache: 'force-cache',
})
// 定期更新 - ISR
const blogPosts = await fetch('/api/posts', {
next: { revalidate: 3600 },
})
// 实时数据 - 不缓存
const liveData = await fetch('/api/live', {
cache: 'no-store',
})
2. 用户个性化程度
// 公共内容 - 可以缓存
const publicPosts = await fetch('/api/posts', {
next: { revalidate: 1800 },
})
// 个性化内容 - 不缓存
const userFeed = await fetch(`/api/feed/${userId}`, {
cache: 'no-store',
})
3. 性能要求
// 首页 - 快速响应
const homepage = await fetch('/api/homepage', {
next: { revalidate: 300 },
})
// 详情页 - 平衡性能和数据新鲜度
const product = await fetch(`/api/products/${id}`, {
next: { revalidate: 3600 },
})
最佳实践
1. 缓存策略组合
// 使用标签 + 时间重新验证
async function OptimizedData() {
const response = await fetch('https://api.example.com/data', {
next: {
revalidate: 3600, // 1小时
tags: ['data']
},
})
return <div>{response}</div>
}
2. 错误处理
async function RobustDataFetch() {
try {
const response = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 },
})
if (!response.ok) {
throw new Error('Failed to fetch data')
}
return <div>{response}</div>
} catch (error) {
return <div>数据加载失败</div>
}
}
总结
Next.js 15 的 fetch 扩展提供了强大的缓存控制:
-
缓存策略:
force-cache:永久缓存no-cache:重新验证但可能使用缓存no-store:完全不缓存
-
重新验证策略:
revalidate:基于时间的重新验证tags:基于标签的重新验证- 组合使用:时间 + 标签
-
管理 API:
revalidatePath:重新验证路径revalidateTag:重新验证标签
这些功能让开发者能够精确控制数据缓存,平衡性能和数据新鲜度。