聊天问答文字逐步显示实现

后端

后端通过事件流逐步发送数据 (SSE),需要设置响应头的Content-Type为'text/event-stream'

SSE是一种服务器推送技术,适合单向流式传输

node示例代码

js 复制代码
const express = require('express');
const app = express();
const port = 3000;

app.get('/stream', (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');
    const text = "嗯,我现在要了解一下合规法规方面的知识。";
    let index = 0;

    const interval = setInterval(() => {
        if (index < text.length) {
            res.write(`data: ${text.charAt(index)}\n\n`); // 逐步发送字符
            index++;
        } else {
            clearInterval(interval);
            res.end(); // 结束流
        }
    }, 100); // 每 100ms 发送一个字符

});

  


app.listen(port, () => {
    console.log(`后端服务运行在 http://localhost:${port}`);
});

前端

1.可以通过EventSource接收流式数据,通过onmessage事件监听数据,并逐字显示

注意:这种方式只能处理get请求,且无法自定义请求头或请求体

示例:

js 复制代码
<body>
    <div id="answer"></div>
    <script>
        const answerElement = document.getElementById('answer');
        // 创建 SSE 连接
        const eventSource = new EventSource('http://localhost:3000/stream');
        // 监听数据事件
        eventSource.onmessage = (event) => {
           answerElement.textContent += event.data; // 逐步显示数据
        };

        // 监听流结束事件
        eventSource.onerror = () => {
            eventSource.close(); // 关闭连接
            console.log('流已结束');
        };
    </script>
</body>

2.使用fetch + ReadableStream

注意:如果是post请求,需要使用fetch。当然XHR也可以通过 progress 事件实现对后端返回的流式数据的解析,但需要注意数据完整性和性能问题。如果需要更强大的流式数据处理能力,推荐使用 fetch + ReadableStream

  1. ReadableStream:

    • 通过 response.body.getReader() 获取 ReadableStream 的读取器。

    • 使用 reader.read() 逐块读取数据。

  2. 解码数据:

    • 使用 TextDecoder 将二进制数据解码为字符串。

示例:

js 复制代码
<body>
  <div id="output"></div>
  <div id="loading">Loading...</div>

  <script>
    const outputElement = document.getElementById('output');
    const loadingElement = document.getElementById('loading');

    async function fetchStreamingData() {
      const url = 'http://localhost:3000/postStream'; // 后端接口地址
      const token = 'your-auth-token'; // 鉴权 token
      const data = { key1: 'value1', key2: 'value2' }; // 请求参数

      loadingElement.style.display = 'block'; // 显示加载动画

      try {
        const response = await fetch(url, {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(data),
        });

        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }

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

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

          const chunk = decoder.decode(value, { stream: true });
          for (const char of chunk) {
            outputElement.textContent += char;
            outputElement.scrollTop = outputElement.scrollHeight; // 自动滚动
          }
        }
      } catch (error) {
        console.error('Error:', error);
        outputElement.textContent = 'Failed to load streaming data.';
      } finally {
        loadingElement.style.display = 'none'; // 隐藏加载动画
      }
    }

    fetchStreamingData();
  </script>
</body>

好啦😊已基本实现啦!当前实际开发中肯定还有一些细节需要注意,比如服务端返回的流式数据的格式,另外大多数内容都是markdown语法,如何把文本的语法格式渲染在html页面上,这些都是需要注意的哟

相关推荐
原则猫8 小时前
UDP 到底怎么实现可靠的
前端
Java后端的Ai之路8 小时前
14、ReactJS - 路由(Router)
前端·react.js·状态模式
裕晟资质规划10 小时前
三类涉密资质延续时限差异分析:军工60日与集成印制3个月的边界
java·服务器·前端·网络·安全
ShineWinsu10 小时前
对于JavaScript:JavaScript基础语法的解析
开发语言·前端·javascript·面试·web·基础语法·循环
月夏10 小时前
Vue3 转 React:18 项对照表 + 5 个必踩的坑(附 7 天上手路线)
前端
广州华水科技10 小时前
单北斗GNSS位移监测系统的应用与维护解析
前端
梦曦i10 小时前
uni-storage 0.1.0 发布:TTL过期管理+多实例隔离
前端·storage·uni-app x
猫头_11 小时前
AI 流式传输工程指南:模型吐出半截 JSON,前端如何边收边渲染?
前端·javascript·llm
console.log('npc')12 小时前
第 1 章 项目概述与环境搭建
前端·人工智能·大模型
定栓12 小时前
Webpack迁到Vite第七节(二):全家桶移除 — Babel 全家桶深度分析
前端·webpack·node.js