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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 13:10:49