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

Next.js 13 App Router中避免SSR与动态元数据重复查询

解决Next.js 13 App路由中数据复用的问题

首先,你用React cache包裹数据获取函数的思路是正确的,在同一个请求周期内(比如一次用户访问),相同参数的cache函数调用会复用结果,不会触发两次API请求。因为cache会缓存异步函数的Promise,generateMetadata和页面组件里的调用会共享同一个Promise,最终只执行一次API请求。

验证缓存是否生效的方法

你可以在FetchingProduct函数里加一行日志,测试是否只执行一次:

const FetchingProduct = cache(async({id})=> {
    console.log(`Fetching product ${id}`) // 添加日志验证
    const response = await fetch(
        `http://127.0.0.1:8000/api/product/${id}`,
        {cache : 'no-store'}
    );
    return response.json()
});

启动项目后访问商品页面,查看服务器终端日志,如果只打印一次,说明缓存已经生效。

更优的实现方案

如果想进一步简化逻辑,避免在两个地方分散调用同一个函数,可以将数据获取逻辑抽离,利用服务器组件的顶层await特性统一获取数据,同时让generateMetadata复用这些逻辑:

import { cache } from 'react'
import ProductDetail from '../components/ProductDetail';

const FetchingProduct = cache(async({id})=> {
    const response = await fetch(
        `http://127.0.0.1:8000/api/product/${id}`,
        {cache : 'no-store'}
    );
    return response.json()
});

const FetchingRecommnandProduct = cache(async({id})=> {
    const response = await fetch(
        `http://127.0.0.1:8000/api/product/recommandation/${id}`,
        {cache : 'no-store'}
    );
    return response.json()
});

// 统一数据获取逻辑,供页面组件和metadata生成函数复用
async function getProductData(id) {
  // 并行发起两个请求,提升加载效率
  const [product, recommandProduct] = await Promise.all([
    FetchingProduct({id}),
    FetchingRecommnandProduct({id})
  ])
  return { product, recommandProduct }
}

export async function generateMetadata({ params }, parent) {
  const { product } = await getProductData(params.id)
  const previousImages = (await parent).openGraph?.images || []
  
  return {
    title: product?.name,
    description: product?.description,
    openGraph: {
      images: [`http://127.0.0.1:8000${product?.images[0]?.image}`, ...previousImages],
    },
  }
}

export default async function Product({ params }) {
  const { product, recommandProduct } = await getProductData(params.id)
  return (
    <div>
      <ProductDetail 
        product={product}
        recommandProduct={recommandProduct}
      />
    </div>
  );
}

这种写法的优势:

  • 用Promise.all并行获取商品数据和推荐商品数据,减少整体请求耗时
  • 统一数据获取逻辑,避免代码分散维护
  • 依然依赖cache保证同请求周期内的API请求复用,不会重复发起调用

注意事项

  • cache函数的缓存是请求级别的,不同用户的请求或者同一用户的不同请求(因为你设置了cache: 'no-store')会重新发起API请求,符合你动态获取数据的需求
  • 客户端组件中调用被cache包裹的异步函数时,必须使用use()钩子,你的原始代码这一点是正确的

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 02:05:25