0

0

React应用中处理数据流:XMLHttpRequest的优化与实践

聖光之護

聖光之護

发布时间:2025-09-01 19:08:01

|

1013人浏览过

|

来源于php中文网

原创

React应用中处理数据流:XMLHttpRequest的优化与实践

本文深入探讨了在React应用中使用XMLHttpRequest处理数据流时遇到的常见问题,特别是数据无法按块实时接收的挑战。核心解决方案在于将onreadystatechange事件替换为更适合跟踪数据接收进度的onprogress事件,并结合setTimeout(0)技巧优化React状态更新,确保UI能够及时响应流式数据。文章还涵盖了如何适应POST请求场景,并推荐了更现代的HTTP请求API。

引言

在现代web应用开发中,实时数据流(streaming data)扮演着越来越重要的角色,例如服务器发送事件(sse)、长轮询或处理大型文件上传/下载进度。当客户端需要接收服务器持续发送的数据块时,正确地处理这些数据流至关重要。本文将聚焦于react应用中,如何利用xmlhttprequest(xhr)有效地接收和处理服务器端的数据流,解决数据一次性全部接收而非按块接收的问题,并提供相应的优化实践

理解数据流与XMLHttpRequest的挑战

在典型的Web应用中,XMLHttpRequest是发起HTTP请求的常用API。对于非流式请求,我们通常在readyState为4(请求完成)时处理响应数据。然而,对于数据流场景,服务器会分批次发送数据,客户端需要能够实时接收这些数据块。

最初尝试使用xhr.onreadystatechange事件来监听数据流可能会遇到问题。尽管readyState为3(正在接收响应)时表示数据正在传输,但在某些浏览器或特定配置下,onreadystatechange事件可能不会在每个数据块到达时都频繁触发,或者在React环境中,状态更新机制可能导致数据累积后才一次性处理。这会导致客户端无法实时获取数据流的最新部分,而是等待整个响应完成才一次性接收所有数据。

考虑以下一个简单的Flask服务器端示例,它每秒发送一个数字:

from flask import Flask, Response
import time

app = Flask(__name__)

@app.route('/stream')
def stream():
    def generate():
        for i in range(10):
            yield f'{i}\n'
            time.sleep(1)
    return Response(generate(), mimetype='text/plain')

if __name__ == '__main__':
    app.run(debug=True)

在React客户端,如果使用如下onreadystatechange的实现,很可能会遇到数据一次性全部接收的问题:

const readStream = () => {
  const xhr = new XMLHttpRequest();
  xhr.seenBytes = 0; // 用于追踪已处理的字节数
  xhr.open('GET', '/stream', true);
  xhr.onreadystatechange = function(){
    if (this.readyState === 3){ // 正在接收响应
      const data = xhr.response.substr(xhr.seenBytes);
      console.log("Received chunk (onreadystatechange):", data);
      // setNumber(data); // 尝试更新状态
      xhr.seenBytes = xhr.responseText.length;
    }
  };
  xhr.send();
};

这种情况下,console.log可能不会按预期每秒输出一个数字,而是等到整个流结束后一次性输出所有数据。

核心解决方案:利用xhr.onprogress事件

为了解决onreadystatechange在数据流处理中的局限性,我们应该利用XMLHttpRequest提供的onprogress事件。onprogress事件专门用于跟踪HTTP请求的进度,它会在浏览器接收到数据时周期性地触发,非常适合处理数据流。

当onprogress事件触发时,xhr.responseText(或xhr.response)会包含所有已接收到的数据。我们可以通过维护一个已处理字节数的计数器(例如xhr.seenBytes),来提取每次onprogress事件触发时新接收到的数据块。

以下是使用onprogress事件进行数据流处理的修正代码:

const readStream = () => {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', '/stream', true);
  xhr.seenBytes = 0; // 初始化已处理字节数
  xhr.onprogress = function(event) {
    // xhr.response 包含所有已接收的数据
    // 使用 substr 从上次处理的位置开始提取新数据
    const newChunk = xhr.response.substr(xhr.seenBytes);
    console.log("Received chunk (onprogress):", newChunk);
    // 更新状态值,稍后会详细讨论React状态更新
    // setNumber(newChunk);
    // 更新已处理字节数
    xhr.seenBytes = xhr.responseText.length;
  };
  xhr.send();
};

通过将onreadystatechange替换为onprogress,并正确维护seenBytes,我们现在能够确保在每个数据块到达时都触发相应的处理逻辑,从而实现数据的实时接收。

React状态更新与流式数据处理

尽管onprogress解决了数据块的实时接收问题,但在React应用中直接调用setNumber(newChunk)可能会遇到另一个挑战。React的批处理(batching)机制和渲染周期可能导致即使onprogress事件频繁触发,UI更新也无法立即反映每个新数据块。例如,如果连续快速接收到多个数据块,React可能会将这些状态更新合并,导致UI看起来仍然是一次性更新。

为了确保React组件能够及时地响应每个数据块并更新UI,我们可以使用setTimeout与0毫秒延迟的技巧。将状态更新操作包裹在setTimeout(..., 0)中,可以将其推迟到当前事件循环的下一个“tick”,从而允许React在每次接收到新数据块后有机会进行渲染。同时,由于数据流是连续的,我们通常需要将新接收到的数据块追加到现有的状态值上,而不是简单地替换它。

Amazon Nova
Amazon Nova

亚马逊云科技(AWS)推出的一系列生成式AI基础模型

下载

以下是结合onprogress和setTimeout的完整React客户端代码示例:

import React, { useState } from 'react';

function StreamingComponent() {
  const [streamedData, setStreamedData] = useState(''); // 用于累加接收到的数据

  const readStream = () => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', '/stream', true);
    xhr.seenBytes = 0; // 初始化已处理字节数

    xhr.onprogress = function(event) {
      // 提取新接收到的数据块
      const newChunk = xhr.response.substr(xhr.seenBytes);
      console.log("Received chunk:", newChunk);

      // 使用 setTimeout 确保状态更新在下一个事件循环中执行,
      // 并使用函数式更新来累加数据
      setTimeout(() => {
        setStreamedData(prevData => prevData + newChunk);
      }, 0);

      // 更新已处理字节数
      xhr.seenBytes = xhr.responseText.length;
    };

    xhr.onload = function() {
      // 流结束时的处理,例如显示“流已完成”
      console.log("Stream finished.");
    };

    xhr.onerror = function() {
      console.error("Stream error.");
    };

    xhr.send();
  };

  return (
    

接收到的数据:

{streamedData}
); } export default StreamingComponent;

在这个优化后的代码中:

  • setStreamedData(prevData => prevData + newChunk) 使用了React函数式状态更新,确保在基于前一个状态值的基础上安全地追加新数据。
  • setTimeout(() => { ... }, 0) 将状态更新操作推迟,允许React在处理完当前事件循环后重新渲染,从而实现更平滑的UI更新。
  • 添加了onload和onerror事件处理器,以更好地处理流的生命周期。

处理POST请求的数据流

在实际应用中,数据流可能需要通过POST请求发送,例如客户端需要发送一些初始化数据或认证信息给服务器。XMLHttpRequest同样支持POST请求的数据流处理。

服务器端(Flask)调整: 将路由方法修改为接受POST请求。

from flask import Flask, Response, request
import time

app = Flask(__name__)

@app.route('/stream', methods=['POST']) # 允许POST请求
def stream():
    # 可以在这里处理request.data或request.json等POST请求体内容
    # 例如:initial_data = request.json
    def generate():
        for i in range(10):
            yield f'POSTed data chunk {i}\n'
            time.sleep(1)
    return Response(generate(), mimetype='text/plain')

if __name__ == '__main__':
    app.run(debug=True)

客户端(React)调整: 修改xhr.open方法,指定为POST请求,并可以在xhr.send()中发送请求体数据。

const readStreamWithPost = () => {
  const xhr = new XMLHttpRequest();
  xhr.open('POST', '/stream', true); // 指定为POST请求
  xhr.seenBytes = 0;

  // 如果需要发送请求体数据,例如JSON
  // xhr.setRequestHeader('Content-Type', 'application/json');
  // const requestBody = JSON.stringify({ initialParam: 'value' });

  xhr.onprogress = function(event) {
    const newChunk = xhr.response.substr(xhr.seenBytes);
    setTimeout(() => {
      setStreamedData(prevData => prevData + newChunk);
    }, 0);
    xhr.seenBytes = xhr.responseText.length;
  };

  xhr.onload = () => console.log("POST Stream finished.");
  xhr.onerror = () => console.error("POST Stream error.");

  // 发送请求体数据,如果不需要则保持为空
  xhr.send(/* requestBody */);
};

通过这些修改,XMLHttpRequest能够以POST方法发起请求并接收服务器返回的数据流。

现代替代方案与最佳实践

尽管XMLHttpRequest在处理数据流方面仍然有效,但在新的Web开发中,更推荐使用现代的API来处理HTTP请求,它们提供了更简洁、更强大的功能,尤其是在处理流方面:

  1. Fetch API: Fetch API是浏览器原生提供的、基于Promise的HTTP请求接口,它提供了更灵活的配置和更易于理解的API。Fetch API结合ReadableStream可以更好地处理数据流。例如,通过response.body.getReader()可以获取一个读取器来逐块读取响应流。

  2. Axios: Axios是一个流行的基于Promise的HTTP客户端,它在Fetch API的基础上提供了更高级的特性,如请求/响应拦截器、取消请求等。虽然Axios本身不直接提供像onprogress那样的流式读取事件(它通常等待响应完成),但可以通过配置responseType: 'stream'或结合onDownloadProgress事件来处理进度,对于真正的ReadableStream支持,可能需要结合Fetch API或更底层的Node.js流API。

对于需要高度控制和细粒度流处理的场景,Fetch API配合ReadableStream是目前最推荐的浏览器端解决方案。

总结

在React应用中处理XMLHttpRequest数据流的关键在于理解其事件机制。通过将监听事件从onreadystatechange切换到onprogress,可以确保数据按块实时接收。结合setTimeout(0)技巧和函数式状态更新,可以有效解决React组件在处理高频数据流时的UI更新延迟问题。无论是GET还是POST请求,XMLHttpRequest的流式处理逻辑都是相似的。尽管如此,对于新的项目或更复杂的流处理需求,Fetch API及其ReadableStream提供了更现代、更强大的解决方案。掌握这些技术,将有助于构建响应更及时、用户体验更好的Web应用。

相关专题

更多
Python Flask框架
Python Flask框架

本专题专注于 Python 轻量级 Web 框架 Flask 的学习与实战,内容涵盖路由与视图、模板渲染、表单处理、数据库集成、用户认证以及RESTful API 开发。通过博客系统、任务管理工具与微服务接口等项目实战,帮助学员掌握 Flask 在快速构建小型到中型 Web 应用中的核心技能。

84

2025.08.25

Python Flask Web框架与API开发
Python Flask Web框架与API开发

本专题系统介绍 Python Flask Web框架的基础与进阶应用,包括Flask路由、请求与响应、模板渲染、表单处理、安全性加固、数据库集成(SQLAlchemy)、以及使用Flask构建 RESTful API 服务。通过多个实战项目,帮助学习者掌握使用 Flask 开发高效、可扩展的 Web 应用与 API。

69

2025.12.15

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

994

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

53

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

239

2025.12.29

js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

508

2023.06.20

js获取当前时间
js获取当前时间

JS全称JavaScript,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言;它是一种属于网络的高级脚本语言,主要用于Web,常用来为网页添加各式各样的动态功能。js怎么获取当前时间呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

241

2023.07.28

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

251

2023.08.03

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

74

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 3.2万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 0.9万人学习

React核心原理新老生命周期精讲
React核心原理新老生命周期精讲

共12课时 | 1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号