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

如何在单次请求中向客户端传输1万条数据无卡顿?可采用流式分块传输吗

解决方案

你要的边接收边使用数据的流式效果,可通过以下两种方案实现,优先推荐第一种,改造成本最低、性能最优:


方案1:HTTP分块流式传输(NDJSON格式)

核心是服务端开启Transfer-Encoding: chunked分块编码,按行传输单条JSON数据(NDJSON格式),客户端逐行解析增量更新数据,无需等待全量响应返回。同时配合数据库流式查询,避免服务端一次性加载全量数据到内存。

服务端代码改造

/**
 * 服务端 dashboard 接口
 */
import { Request, Response } from 'express';

public dashboard = async (req: Request, res: Response) => {
  // 设置流式响应头
  res.setHeader('Content-Type', 'application/x-ndjson; charset=utf-8');
  res.setHeader('Transfer-Encoding', 'chunked');
  res.status(200);

  try {
    // knex开启流式查询,不会一次性加载全量数据到服务端内存
    const dataStream = knex('dashboard').select().stream();
    
    for await (const row of dataStream) {
      // 单条数据转JSON后加换行符分隔,写入响应流
      res.write(`${JSON.stringify(row)}\n`);
    }
    // 所有数据传输完成,结束响应
    res.end();
  } catch (err) {
    res.write(`${JSON.stringify({ error: err.message })}\n`);
    res.end();
  }
};

客户端MobX存储改造

/**
 * 客户端 MobX 存储
 */
import axios from 'axios';
import { makeAutoObservable, runInAction } from 'mobx';

class Store {
  data = [];
  constructor() {
    makeAutoObservable(this);
  }

  public getData = async () => {
    try {
      const response = await axios({
        url: 'http://localhost:3000/dashboard',
        method: 'GET',
        // 开启流式响应支持,axios >= 1.4.0 版本可用fetch适配器
        responseType: 'stream',
        adapter: 'fetch'
      });

      const decoder = new TextDecoder('utf-8');
      const reader = response.data.getReader();
      let buffer = '';

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        // 解码二进制流并拼接到缓冲区
        buffer += decoder.decode(value, { stream: true });
        // 按换行符拆分完整数据行
        const lines = buffer.split('\n');
        // 保留不完整的行到下一次解析
        buffer = lines.pop() || '';

        // 逐行解析数据,增量更新到存储
        for (const line of lines) {
          if (!line.trim()) continue;
          try {
            const row = JSON.parse(line);
            if (row.error) throw new Error(row.error);
            runInAction(() => {
              this.data.push(row);
            });
          } catch (parseErr) {
            console.error('数据行解析失败:', parseErr);
          }
        }
      }
    } catch (err) {
      console.error(err.message || err);
    }
  };
}

如果axios版本低于1.4.0不支持fetch适配器,可直接替换为原生fetch实现流式请求,解析逻辑完全一致。

前端组件优化

现有React组件无需修改即可自动响应数据增量更新,建议给Table开启虚拟滚动,避免数据量过大导致DOM渲染卡顿:

<Table
  rowKey="id"
  className={`home__table`}
  columns={columns}
  dataSource={data}
  // 开启虚拟滚动,只渲染可视区域的行
  scroll={{ y: 600 }}
  virtual
/>

方案2:分页拉取(备选)

如果不想改造流式逻辑,可实现无限滚动分页:服务端新增page、pageSize参数支持分页查询,前端首次拉取前100条数据直接渲染,滚动到列表底部再自动拉取下一页。该方案兼容性更强、逻辑更简单,唯一不足是需要前端额外处理分页状态。


注意事项

  • 流式传输时不要开启gzip压缩,部分代理服务器会缓存gzip内容导致流式失效,需要压缩可使用brotli流式压缩
  • 1万条数据量级下开启Table虚拟滚动可大幅降低渲染性能消耗,避免页面卡顿

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 08:15:08