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

React中map遍历组件内fetch请求的规范实现与优化方案

问题根因

当前实现的核心问题是所有列表项挂载即触发请求,数百条并发请求会占满浏览器同域名的并发连接配额(常规浏览器同域名仅支持6-8个并行TCP连接),后续请求会排队阻塞,轻则首屏加载慢,重则出现请求超时、页面无响应。
另外你的现有代码还有两个显性bug:

  • App组件里map遍历的箭头函数加了大括号但没有return,实际不会渲染任何FruitContainer组件
  • FruitContainer接收props时没有解构,直接写(fruit)拿到的是整个props对象,无法读取到fruit.url属性
可选实现方案

根据你的业务场景二选一即可,都能解决并发请求过多的问题,且代码可维护性强。

方案1:虚拟滚动+可视区按需请求(长列表场景首选)

这是百条以上长列表的标准优化方案,从根源上控制同时挂载的组件数量,非可视区域的组件不会渲染,自然不会发起请求。

  • 核心逻辑:
    • 用成熟的虚拟滚动能力,仅渲染当前可视区域+上下缓冲区域的列表项,DOM节点总数始终控制在10-20个,既解决请求并发问题,也避免长列表DOM过多导致的页面卡顿
    • 只有当列表项滚动到可视范围内时,FruitContainer才会挂载,触发对应的数据请求
  • 代码参考:
// Index.js
import { FixedSizeList } from 'react-window';
import FruitContainer from './FruitContainer';

const fruits = [
  { name: "apple", url: "http://foo" },
  { name: "banana", url: "http://bar" },
  // 数百条其余数据
];

// 单行渲染逻辑
const Row = ({ index, style }) => (
  <div style={style}>
    <FruitContainer fruit={fruits[index]} />
  </div>
);

const App = () => {
  return (
    <FixedSizeList
      height={600} // 列表可视区域总高度
      itemCount={fruits.length}
      itemSize={80} // 单个列表项固定高度
      width="100%"
    >
      {Row}
    </FixedSizeList>
  );
};

方案2:全局请求并发控制(需渲染全量DOM时使用)

如果你的业务要求所有列表项DOM必须全量渲染(比如需要支持全页文本搜索、全量打印),就把请求逻辑从单个组件抽离,做统一的并发调度,固定同时执行的请求数量(通常设为3-5即可),所有请求排队执行。

  • 核心逻辑:
    1. 实现一个通用的请求池,支持配置最大并发数,请求按提交顺序排队,当前面的请求结束后自动执行下一个排队任务
    2. 列表项不再直接调用fetch,而是把请求任务提交到请求池,由调度器控制执行时机
  • 代码参考:
    首先实现通用并发请求池:
// requestPool.js
export const createRequestPool = (maxConcurrent = 4) => {
  const taskQueue = [];
  let runningCount = 0;

  const runTask = async (task, resolve, reject) => {
    runningCount++;
    try {
      const result = await task();
      resolve(result);
    } catch (err) {
      reject(err);
    } finally {
      runningCount--;
      if (taskQueue.length) {
        const nextTask = taskQueue.shift();
        runTask(nextTask.task, nextTask.resolve, nextTask.reject);
      }
    }
  };

  return (requestTask) => {
    return new Promise((resolve, reject) => {
      if (runningCount < maxConcurrent) {
        runTask(requestTask, resolve, reject);
      } else {
        taskQueue.push({ task: requestTask, resolve, reject });
      }
    });
  };
};

// 初始化请求池实例,配置最大并发数为4
export const limitedRequest = createRequestPool(4);

修改列表项组件的请求逻辑:

// FruitContainer.js
import { useState, useEffect } from 'react';
import { limitedRequest } from './requestPool';

// 修复props解构问题
const FruitContainer = ({ fruit }) => {
  const [fruitData, setFruitData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // 提交请求到并发池,排队执行
    limitedRequest(() => fetch(fruit.url).then(res => res.json()))
      .then(data => {
        setFruitData(data);
        setLoading(false);
      })
      .catch(err => {
        console.error('数据加载失败', err);
        setLoading(false);
      });
  }, [fruit.url]);

  if (loading) return <div>加载中...</div>;
  return (
    <>
      <Text>{fruitData?.name}</Text>
      <Text>{fruitData?.color}</Text>
    </>
  );
};
额外优化建议
  • 增加请求缓存:对已请求过的url结果做内存缓存,同url二次请求直接返回缓存结果,避免组件卸载重挂载时重复发请求
  • 增加预加载:如果用虚拟滚动方案,可以提前预加载可视区下方1-2屏的数据,降低用户滚动时的等待感
  • 错误兜底:给请求加失败重试、错误提示逻辑,避免单个请求失败导致页面展示异常

内容的提问来源于stack exchange,提问作者Liiaam93

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 07:06:10