Q9: Next.js 15 对 fetch 的扩展:解释 fetch 的缓存策略(cache: 'force-cache', 'no-cache', 'no-

85 阅读3分钟

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 扩展提供了强大的缓存控制:

  1. 缓存策略

    • force-cache:永久缓存
    • no-cache:重新验证但可能使用缓存
    • no-store:完全不缓存
  2. 重新验证策略

    • revalidate:基于时间的重新验证
    • tags:基于标签的重新验证
    • 组合使用:时间 + 标签
  3. 管理 API

    • revalidatePath:重新验证路径
    • revalidateTag:重新验证标签

这些功能让开发者能够精确控制数据缓存,平衡性能和数据新鲜度。