Next.js同请求内fetch未缓存,如何实现请求复用?
解决Next.js中generateMetadata与组件内fetch请求重复调用的问题
你遇到的问题是在App Router的服务端组件中,generateMetadata和组件本身两次调用同一API却触发了两次请求,这和Next.js官方文档描述的请求缓存复用不符。下面是具体的原因分析和解决方法:
问题原因
虽然Next.js默认会在同一请求周期内对相同参数的fetch请求进行去重缓存,但以下情况可能导致缓存失效:
- 请求的URL、headers、方法等参数存在细微差异(比如环境变量加载不一致)
- 本地开发模式下的缓存策略与生产环境有差异
- 未显式指定缓存策略,导致Next.js的自动去重未生效
解决方法:抽离逻辑并显式缓存
最可靠的方式是将获取文章的逻辑抽离为单独函数,并用Next.js提供的unstable_cache包裹,确保两次调用复用同一缓存结果。
修改后的完整代码
// posts/[slug]/page.tsx import { Metadata } from "next"; import { unstable_cache } from 'next/cache'; import { PostResponse } from "@/service/model/api"; // 抽离获取文章的逻辑,并用unstable_cache缓存结果 const getPostBySlug = unstable_cache( async (slug: string) => { const response = await fetch(`${process.env.BASE_URL}/api/posts/slug?slug=${slug}`, { method: "GET", headers: { "Authorization": `Bearer ${process.env.API_KEY}` }, cache: 'force-cache' // 显式指定强制缓存策略 }); if (!response.ok) { throw new Error(`Failed to fetch post: ${response.statusText}`); } const posts: PostResponse = await response.json(); return posts.posts[0]; }, ['post-by-slug'], // 缓存键的唯一标识前缀 { revalidate: 60 } // 可选:设置缓存自动刷新时间(单位:秒) ); export async function generateMetadata(props: PostPageProps): Promise<Metadata> { const post = await getPostBySlug(props.params.slug); return { title: post.title }; } export default async function PostPage(props: PostPageProps) { const post = await getPostBySlug(props.params.slug); return ( <div> <p>{post.title}</p> </div> ); } export interface PostPageProps { params: PostPageParams; } interface PostPageParams { slug: string; text?: string; }
额外注意事项
- 确保两次调用的请求参数完全一致:包括URL、headers、请求方法等,任何差异都会导致缓存不生效
- 本地开发时若仍有问题,可以尝试重启开发服务器,确保环境变量加载一致
unstable_cache目前处于不稳定状态,但在App Router中是实现请求复用的可靠方式,后续Next.js可能会提供更稳定的API替代
内容的提问来源于stack exchange,提问作者Maxime Esnol
相关产品推荐
相关产品推荐

