You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js SSR缓存存储范围及分页排序可扩展方案咨询

问题解答

一、Cache-Control 缓存的内容

你设置的 Cache-Control 会缓存当前请求对应的切片后数据,也就是对应某一页的6条产品数据,而非拉取的全部API数据。

因为每个请求的 page 参数不同,对应的请求URL(比如 /products?page=1 和 /products?page=2)是不同的,CDN或服务器会根据完整URL作为缓存键,所以每一页的结果会被单独缓存。但要注意:你的当前代码中,每次执行 getServerSideProps 都会重新调用 getDataFromApi() 拉取全部数据,缓存只作用于最终返回的页面/ props,并没有减少对上游API的请求量。

二、针对大数量级数据的可扩展方案

针对数据量达数千条的场景,结合你的排序需求(推荐产品在前,各自按字母排序),可以从以下几个方向优化:

1. 缓存全量排序后的产品数据

核心思路是避免每次请求都重复拉取上游API、重复排序,而是把处理好的全量排序数据缓存起来,后续所有分页请求直接从缓存取数据切片。

示例代码(用内存缓存,生产环境建议用Redis等分布式缓存):

// 全局缓存变量,生产环境建议用Redis替代
let cachedSortedProducts = null;
let cacheExpiry = 0;

export const getServerSideProps = async (ctx) => {
  ctx.res.setHeader(
    'Cache-Control',
    'public, s-maxage=60, stale-while-revalidate=120'
  );

  const page = parseInt(ctx.query.page) || 1;
  const itemsPerPage = 6;
  const cacheTTL = 3600; // 缓存1小时

  // 检查缓存是否有效
  if (!cachedSortedProducts || Date.now() > cacheExpiry) {
    // 仅缓存过期时才调用API
    const products = await getDataFromApi();
    const featured = products.filter(isFeatured).sort(alphabetical);
    const notFeatured = products.filter(isNotFeatured).sort(alphabetical);
    cachedSortedProducts = [...featured, ...notFeatured];
    cacheExpiry = Date.now() + cacheTTL * 1000;
  }

  // 从缓存数据中切片分页
  const startIndex = (page - 1) * itemsPerPage;
  const endIndex = startIndex + itemsPerPage;
  const paginatedProducts = cachedSortedProducts.slice(startIndex, endIndex);

  return {
    props: {
      products: paginatedProducts
    }
  };
};

2. 利用上游API的排序能力减少服务端处理

既然上游API支持单字段排序,可以拆分请求:

  • 先调用API拉取所有 featured=true 的产品,要求按字母排序(用API支持的排序参数)
  • 再调用API拉取所有 featured=false 的产品,同样按字母排序
  • 将这两部分结果合并后缓存,后续分页直接从合并后的缓存数据切片

这样可以避免在服务端对全量数据做filter和sort,减少服务端的CPU和内存消耗,同时拆分的API请求也可能比拉全量更快。

3. 切换到增量静态生成(ISR)

如果产品数据不是实时更新的(比如更新频率低于1小时),可以用Next.js的ISR预先生成所有分页页面,彻底避免服务器端每次请求都处理数据:

export const getStaticPaths = async () => {
  // 先拉取全量数据计算总页数
  const products = await getDataFromApi();
  const featured = products.filter(isFeatured).sort(alphabetical);
  const notFeatured = products.filter(isNotFeatured).sort(alphabetical);
  const sortedProducts = [...featured, ...notFeatured];
  const totalPages = Math.ceil(sortedProducts.length / 6);

  // 生成所有分页路径
  const paths = Array.from({ length: totalPages }, (_, i) => ({
    params: { page: (i + 1).toString() }
  }));

  return { paths, fallback: 'blocking' };
};

export const getStaticProps = async ({ params }) => {
  const page = parseInt(params.page);
  const itemsPerPage = 6;

  const products = await getDataFromApi();
  const featured = products.filter(isFeatured).sort(alphabetical);
  const notFeatured = products.filter(isNotFeatured).sort(alphabetical);
  const sortedProducts = [...featured, ...notFeatured];

  const startIndex = (page - 1) * itemsPerPage;
  const endIndex = startIndex + itemsPerPage;
  const paginatedProducts = sortedProducts.slice(startIndex, endIndex);

  return {
    props: { products: paginatedProducts },
    revalidate: 3600 // 每小时重新生成页面
  };
};

4. 客户端缓存优化方案

如果担心服务端压力,也可以回到客户端拉取方案,但用缓存库(如React Query)优化:

  • 首次加载时拉取全量产品数据,用React Query缓存到内存+本地存储
  • 客户端直接在缓存数据上执行排序和分页逻辑
  • 后续切换页面无需再请求API,直接从缓存取数据处理

数千条数据的体积通常在几MB级别,对现代浏览器来说完全可承受,且React Query会自动处理缓存过期、后台刷新等逻辑,用户体验也能得到保障。

内容的提问来源于stack exchange,提问作者muhammadarifftaha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 15:36:39