前端处理流式数据(SSE服务)

前言

将数据用流的方式返回给客户端,这种技术需求在传统的管理项目中不多见,但是在媒体或者有实时消息等功能上就会用到,这个知识点对于前端还是很重要的。

即时你不写服务端,但是服务端如果给你这样的接口,你也得知道怎么去使用联调。

nodejs实现简单的SSE服务

SSE服务(Server-Sent Events),是一种服务器向客户端推送实时更新的机制模式。

const express = require('express');  
const app = express();  
const port = 8002;  


let strArr = [
    '所有人,都得死!',
    '犯我德邦者,虽远必诛!',
    '吾所成之事,不可逆也!',
    '一点寒芒先到,随后枪出如龙!',
    '我的剑就是你的剑!',
    '刀下生,刀下死!'
]
let setTask = null
  
app.get('/events', (req, res) => {  
  res.setHeader('Content-Type', 'text/event-stream;charset=utf-8');  
  res.setHeader('Cache-Control', 'no-cache');  
  res.setHeader('Connection', 'keep-alive');  
  let num = 0
  setTask =  setInterval(()=>{
    res.write(`data:${strArr[num]}\n\n`)
    num++
    if(num > 5){
        res.write(`data:end\n\n`)
        res.end()
        // res.closed()
        clearInterval(setTask)
        setTask = null
    }
  },1000)
});  
  
app.listen(port, () => {  
  console.log(`${port}端口已启动`);  
});

效果如下

 前端实现接收数据流:

这里使用一个叫做EventSource的api去实现流式接口的调用和数据获取

配置代理(重要)

如果我们用vue,react等等框架开发时,需要在代理处做一些配置,确保数据会以流式的返回。如果不做这层代理的配置,那么你获取的数据就会是执行完所有的res.write,一次性的全部返回给前端,就不是我们想要的效果。

效果如下,在配置代理中将compress设置为false

    devServer:{
      client:{
        overlay:false
      },
      port:8080,
      open:true,
      compress:false,  //流式数据返回的关键配置
      proxy:{
        '/server1':{
          target:'http://localhost:3001',
          ws:false,
          changeOrigin:true,
          pathRewrite:{
            '^/server1':''
          }
        },
        '/server2':{
          target:'http://localhost:3002',
          ws:false,
          changeOrigin:true,
          pathRewrite:{
            '^/server2':''
          }
        },
        '/sse':{
          target:'http://localhost:8002',
          ws:false,
          changeOrigin:true,
          pathRewrite:{
            '^/sse':''
          }
        }
      }
    }

之前没有配置这里,导致数据一直是一次性返回的,困了我一会,后来查找原因,借鉴的这篇文章

fetchEventSource请求,数据没有流式输出_vue2 eventsource为什么没有流式输出-CSDN博客

前端实现接口调用
<template>
    <div>
        <el-button @click="sendMsg">发送消息</el-button>
        <p v-for="(item,index) in msgList" :key="index">{{ item }}</p>
    </div>
   
  </template>
  <script>
   export default{
      name:'admin',
      data(){
        return{
            msgList:[]
        }
      },
      methods:{
    sendMsg(){
        let vm = this

    //方案1:EventSource
      const eventSource = new EventSource('/sse/events');  
  //消息监听
  eventSource.onmessage = function(event) {  
    console.log(eventSource,vm,'状态')
    console.log(event.data); // 输出SSE发送的数据  
    if(event.data === 'end'){
      eventSource.close()
    }else{
      vm.msgList.push(event.data)
    }
 
  };  
  //连接成功
  eventSource.onopen = function(event){

  }
  //连接出错
  eventSource.onerror = function(error) {  
    if (eventSource.readyState === EventSource.CLOSED) {  
      // 连接已关闭,可能需要重新连接  
      console.error('SSE连接已关闭:', error);  
    }  
  }

  //方案2:xhr(不推荐)
  // const xhr = new XMLHttpRequest(); 
  // const url = '/sse/events'; 
  // xhr.open('GET', url,true); 
  // xhr.setRequestHeader('Accept', 'text/event-stream');

  // xhr.onload = (event)=>{
  //   if(xhr.status === 200){
  //     console.log(xhr.responseText,'onload',event)
  //   }
  // }

  // xhr.onreadystatechange = (event)=>{
  //   // if(xhr.status === 200){
  //   //   console.log(xhr.responseText,'onreadystatechange',event)
  //   // }
    
  // }
  // xhr.onprogress = (event)=>{
  //   if(xhr.status === 200){
  //     console.log(xhr.responseText,'onreadystatechange',event)
     
  //   }
  // }
  // xhr.send()
    }
      }
   }
  </script>
  <style lang="less">
  </style>

 这样就大功告成了,如果以后要是做类似于chatgpt这种效果,就可以用到的。

感觉有用的给个三连吧!

<think>好的,我现在需要帮用户解决如何在前端处理流数据的文本样的问题。首先,我得回顾一下用户提供的引用内容,看看有哪些相关的信息。用户提到了引用1到4,其中涉及到流数据的处理方,比如使用Stream、EventSource、SSE,以及如何处理markdown格数据和样设置。 用户的问题主要集中在文本样的处理上,特别是在流数据的情况下。首先,我需要明确流数据前端的常见处理方,比如使用Fetch API、SSE或者WebSocket。根据引用2和3,SSE(Server-Sent Events)是一个常用的方法,因为它允许服务器主动推送数据到客户端,适合实时更新。而引用4提到处理markdown转换的问题,可能需要将流接收的markdown内容实时转换为HTML并应用样。 接下来,步骤可能包括:1. 数据流的获取方,2. 数据的分块处理,3. 实时渲染到页面,4.的应用。例如,使用SSE建立连接,监听消息事件,每收到一个数据块就进行处理。对于纯文本,可以直接追加到DOM元素中;如果是markdown,则需要转换,比如使用marked.js库,并在转换后应用自定义的CSS样。 还需要考虑如何分块处理数据,避免界面卡顿。可能使用requestAnimationFrame或者微任务来优化渲染性能。另外,针对引用4提到的深度思考内容,可能需要特定的CSS类名来添加特殊样,比如不同的背景色或字体样。 还要注意错误处理和连接关闭的情况,确保用户体验良好。最后,可能需要根据不同的数据(如JSON、纯文本、markdown)采取不同的处理策略,确保样正确应用。 总结一下,处理流程大致是:建立流连接→分块接收数据→解析处理(如markdown转HTML)→动态插入DOM→应用样。需要结合具体的库和技术实现,同时优化渲染性能,确保流畅显示。</think>### 前端数据文本样处理方法 #### 一、流数据接收方 1. **SSE(Server-Sent Events)** ```javascript const eventSource = new EventSource('/api/stream'); eventSource.onmessage = (event) => { processChunk(event.data); // 处理数据块 }; ``` 推荐使用`event-source-polyfill`库兼容旧浏览器[^3] 2. **Fetch API流读取** ```javascript fetch('/api/stream') .then(response => { const reader = response.body.getReader(); const decoder = new TextDecoder(); function read() { reader.read().then(({ done, value }) => { if (done) return; const chunk = decoder.decode(value); processChunk(chunk); read(); }); } read(); }); ``` #### 二、样处理核心步骤 1. **动态内容分片** ```javascript function processChunk(chunk) { const container = document.getElementById('output'); const span = document.createElement('span'); span.className = 'stream-chunk'; // 添加样类 span.textContent = chunk; container.appendChild(span); } ``` 2. **Markdown实时转换**(适用于返回markdown格的场景) ```javascript import { marked } from 'marked'; function renderMarkdownChunk(chunk) { const html = marked.parse(chunk); // Markdown转HTML const div = document.createElement('div'); div.className = 'markdown-content'; div.innerHTML = html; document.body.appendChild(div); } ``` 3. **特殊内容标记** ```css /* 示例:为"深度思考"内容添加特殊样 */ .deep-think { background: #f0f5ff; border-left: 4px solid #1890ff; padding: 12px; margin: 8px 0; } ``` #### 三、性能优化技巧 1. **节流渲染**:使用`requestAnimationFrame`批量更新 ```javascript let buffer = []; function scheduleRender() { requestAnimationFrame(() => { document.getElementById('output').textContent += buffer.join(''); buffer = []; }); } ``` 2. **语法高亮**(代码场景) ```javascript import hljs from 'highlight.js'; function highlightCodeBlocks() { document.querySelectorAll('pre code').forEach(block => { hljs.highlightElement(block); }); } ``` #### 四、特殊场景处理 1. **混合内容处理**(文字+控制符) ```javascript function parseSpecialSymbols(text) { return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>') .replace(/__(.*?)__/g, '$1'); } ``` 2. **光标跟随滚动** ```javascript function autoScroll() { const container = document.getElementById('output'); container.scrollTop = container.scrollHeight; } ``` ### 引用说明 流数据处理需要结合SSE协议特性实现渐进渲染[^2],对于Markdown等结构化内容需使用专用解析库保证样准确性[^4]。特殊样定义建议通过CSS类名隔离控制,避免全局样污染[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值