数据预取时怎么避免重复请求?

爱学习的羽铮 阅读 10

我在用 React 做一个商品详情页,进入列表页的时候想提前把详情数据拉下来,用了 useEffect 里调接口做预取。但用户如果快速点进同一个商品两次,发现控制台发了两次一样的请求,这不就浪费了吗?

我试过加个 loading 状态判断,但预取是在另一个组件里做的,状态不好共享。也想过用全局缓存,但不确定怎么设计才靠谱。有没有比较通用的做法?比如用 axios 的拦截器或者封装一个带缓存的 fetch?

这是我现在预取的代码:

useEffect(() => {
  const prefetchProduct = async (id) => {
    const res = await fetch(<code>/api/product/${id}</code>);
    const data = await res.json();
    // 存到 context 或 zustand 里
    setPrefetchedData(id, data);
  };

  products.forEach(p => prefetchProduct(p.id));
}, [products]);

怎么才能确保同一个 id 只请求一次啊?

我来解答 赞 5 收藏
二维码
手机扫码查看
1 条解答
Des.乙涵
这问题太常见了,简单做法是用 Map 存一下请求状态,同一个 id 的请求直接复用:

// 放在组件外面,或者抽成工具文件
const requestCache = new Map();

const prefetchProduct = async (id) => {
const url = /api/product/${id};

// 已经有 pending 或已完成的请求,直接复用
if (requestCache.has(url)) {
return requestCache.get(url);
}

const promise = fetch(url).then(res => res.json());
requestCache.set(url, promise);

return promise;
};

// 使用useEffect(() => {
products.forEach(p => {
prefetchProduct(p.id).then(data => {
setPrefetchedData(p.id, data);
});
});
}, [products]);


这样同一个 id 无论触发多少次 fetch,底层只发一次请求,后续的直接等那个 promise。

如果你还想缓存已请求过的数据(不只是 pending 状态),可以改成这样:

const dataCache = new Map();

const fetchProduct = async (id) => {
const url = /api/product/${id};

// 先检查内存缓存
if (dataCache.has(url)) {
return dataCache.get(url);
}

// 检查是否有 pending 请求
if (requestCache.has(url)) {
return requestCache.get(url);
}

const promise = fetch(url).then(res => res.json());
requestCache.set(url, promise);

const data = await promise;
dataCache.set(url, data); // 存下来,后续直接返回
requestCache.delete(url);

return data;
};


如果你项目里用了 axios,思路是一样的,拦截器里判断一下就行。不过上面这个原生 fetch 的方案够用了,逻辑清晰也容易维护。
点赞
2026-03-16 14:45