Next.js13 Server Component重验证时fetch调用两次问题求助
问题解释与解决办法
这不是Next.js 13的bug,而是ISR(增量静态再生)机制的正常行为:当启用revalidate进行页面重验证时,Next.js会在后台预先生成新的页面版本,确保内容有效后再替换旧版本,这个过程中会触发两次数据获取——一次用于生成新页面,一次是缓存校验或页面渲染的兜底逻辑。即使关闭了reactStrictMode,这种后台预生成的机制依然会运行。
如果担心重复查询数据库带来的性能损耗,可以通过以下两种方式解决:
1. 手动添加内存缓存
在数据获取函数中加入本地缓存,让两次调用共享同一份查询结果:
let cachedUsers = null; let cacheExpiry = 0; async function getUsers() { const now = Date.now(); // 缓存有效期设为19秒,比revalidate短1秒,确保重验证时能获取最新数据 if (cachedUsers && now < cacheExpiry) { return cachedUsers; } const client = await clientPromise; const db = client.db("local"); const data = await db .collection("users") .find({ email: "bob123@gmail.com" }) .toArray(); console.log("THIS IS THE SERVER RUNNING"); cachedUsers = data; cacheExpiry = now + 19000; return data; }
2. 使用Next.js内置缓存API
利用Next.js提供的unstable_cache(注:这是实验性API,后续版本可能调整)自动处理缓存逻辑,避免重复执行数据库查询:
import { unstable_cache } from 'next/cache'; // 用unstable_cache包裹数据获取函数,指定缓存键和重验证时间 const getUsers = unstable_cache( async () => { const client = await clientPromise; const db = client.db("local"); const data = await db .collection("users") .find({ email: "bob123@gmail.com" }) .toArray(); console.log("THIS IS THE SERVER RUNNING"); return data; }, ['users-bob123'], // 唯一缓存标识,用于区分不同查询 { revalidate: 20 } // 重验证周期,与页面的revalidate保持一致 ); export default async function Home() { const users = await getUsers(); if (!users) return notFound(); return ( <main className={styles.main}> {users?.map((user) => ( <p key={user._id}>{user.name}</p> ))} </main> ); }
通过以上两种方式,即使函数被调用两次,实际的数据库查询只会执行一次,不会造成额外的资源消耗。
内容的提问来源于stack exchange,提问作者RiskItAll
相关产品推荐
相关产品推荐

