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
相关产品推荐
相关产品推荐

