vue使用post请求处理接口返回EventStream数据并进行展示

之前已经发过简单使用get请求接口EventStream数据:
vue处理接口返回EventStream数据并进行展示

使用post请求接口EventStream数据具体操作如下:

使用sse.js第三方插件实现:
安装sse.js:

npm install sse

vue页面代码:

import { SSE } from 'sse.js';
import { parseMarkdown } from '@/utils/markdownParser'
export default {
	name: "chat",
	data() {
		return {
			sseFun: null,
		};
	},
	methods: {
		// 使用 sse.js 实现
		connectSSEJS(data){
			let token = window.sessionStorage.getItem('token');
			let para = {
				参数1: 参数1的值,
				参数1: 参数2的值,
			}
			this.sseFun = new SSE(`接口地址`, {
				method: 'POST',
				payload: JSON.stringify(para),
				headers: {
					'Content-Type': 'application/json;charset=utf-8',
					'token': `${token}`
				},
			});
			let that = this;
			let _webSearchResultList = [];
			this.sseFun.onmessage = (event) => {
				// console.log('收到消息:', event.data);
				let _data= JSON.parse(this.decodeBase64ToUtf8(event.data));
				if(_data.state==='start'){
					// 接收数据的第一条标识:state为start(具体根据接口返回的情况进行判断),可储存或处理一些数据
				}
				// 对接收到的数据进行处理
				let dealTxt = String(_data.message);
				const newText = dealTxt.replace(/\$ref_(\d+)/g, (match, number) => {
					return `<span class="webmark" style="background: rgb(255, 232, 230);
					color: rgb(230, 0, 19);
					padding: 1px 8px;
					border-radius: 10px;
					margin-right: 4px;" 
					data-link="${_webSearchResultList.find(i=>i.refer == match).link}" 
					data-title="${_webSearchResultList.find(i=>i.refer == match).title}">网页 ${number}</span>`;
				});
				// 此处的parseMarkdown用于解析和渲染Markdown文本,具体代码在下面
				this.streamText = parseMarkdown(String(newText.replace(/\\\[/g, '$$$').replace(/\\\]/g, '$$$')));
				if(!_data.id){
					// 不存在id,认为数据返回完成
				}
			};
		},
		// 断开SSE连接
		disconnectSSE() {
			if (this.sseFun) {
				this.sseFun.close(); // 关闭SSE连接
				this.sseFun = null; // 清除引用,防止内存泄漏
			}
		},
	}
}

用于解析和渲染Markdown文本的markdownParser文件内容:

import MarkdownIt from 'markdown-it'
import mk from 'markdown-it-katex';  // 匹配数学公式
import sanitizer from "markdown-it-sanitizer";
import hljs from 'highlight.js'; // 匹配代码
import DOMPurify from 'dompurify'

// 初始化 Markdown 解析器
const md = new MarkdownIt({
  html: true,         // 允许 HTML 标签
  linkify: false,      // 自动转换链接
  typographer: true,  // 优化排版
  tabels: true,       // 允许表格

  highlight: (str, lang) => {  // 代码高亮
    // 代码高亮逻辑
    if (lang && hljs.getLanguage(lang)) {
      try {
        return `<pre class="hljs"><code>${
          hljs.highlight(str, { language: lang, ignoreIllegals: true }).value
        }</code></pre>`;
      } catch (__) {}
    }
    // 未指定语言时,自动检测
    return `<pre class="hljs" style="background: #fafafa;"><code>${hljs.highlightAuto(str).value}</code></pre>`;
  }
})
md.use(mk, {
  macros: {
    // 物理符号
    "\\e": "\\mathrm{e}",
    "\\ii": "\\mathrm{i}",
    "\\dd": "\\mathop{}\\!\\mathrm{d}",
    "\\bra": ["\\left\\langle #1 \\right|", 1],
    "\\ket": ["\\left| #1 \\right\\rangle", 1],
    "\\vect": ["\\mathbf{#1}", 1],
    "\\tensor": ["\\underline{\\underline{#1}}", 1],
    "\\ev": ["\\left\\langle #1 \\right\\rangle", 1],
    
    // 数学符号
    "\\Z": "\\mathbb{Z}",
    "\\Q": "\\mathbb{Q}",
    "\\C": "\\mathbb{C}",
    "\\R": "\\mathbb{R}",
    "\\abs": ["\\left| #1 \\right|", 1],
    "\\pd": ["\\frac{\\partial #1}{\\partial #2}", 2],
    "\\matrix": "\\begin{pmatrix} #1 \\\\ #2 \\end{pmatrix}",
    "\\pmatrix": ["\\begin{pmatrix} #1 \\\\ #2 \\end{pmatrix}", 2],
    "\\eq": ["\\begin{equation} #1 \\end{equation}", 1],

    // 化学
    "\\xrightarrow": ["\\overset{#2}{\\longrightarrow}", 2],
    "\\chem": ["\\mathrm{#1}", 1],

    // 统计力学
    "\\kB": "k_{\\text{B}}",
    "\\mean": ["\\left\\langle #1 \\right\\rangle", 1],
    "\\var": ["\\operatorname{Var}(#1)", 1],
    "\\cov": ["\\operatorname{Cov}(#1, #2)", 2],
    "\\dB": ["\\, \\text{dB}", 0],

    // 动态宏
    "\\unit": (ctx) => {
      const args = ctx.consumeArgs(1);
      return `\\, \\text{${args[0]}}`;
    },
    "\\grad": (context) => {
      const args = context.consumeArgs(1); // 读取参数
      return `\\nabla ${args[0]}`;
    },
  },
  throwOnError: false,
  displayMode:true
}); // 启用 KaTeX 插件

// 自定义链接处理(设置 target="_blank")
md.renderer.rules.link_open = (tokens, idx, options) => {
  const token = tokens[idx]
  const hrefIndex = token.attrIndex('href')
  if (hrefIndex >= 0) {
    token.attrPush(['target', '_blank'])
    token.attrPush(['rel', 'noopener noreferrer'])
  }
  return md.renderer.renderToken(tokens, idx, options)
}

// 解析并净化 Markdown
export function parseMarkdown(content) {
  const rawHtml = md.render(content)
  return DOMPurify.sanitize(rawHtml)
}
### 处理从后端接收的 Stream 流 为了在 JavaScript 或 TypeScript 中处理来自服务器发送事件(SSE)或其他形式的流数据,可以采用多种方式。对于 SSE 特定情况下的实现如下: 创建一个新的 `EventSource` 实例来连接到支持 SSE 的 API 端点,监听消息事件以便实时处理传入的数据[^2]。 ```javascript const eventSource = new EventSource('/api/sse'); eventSource.onmessage = function(event) { const data = JSON.parse(event.data); console.log(`Received message type ${data.t} with content:`, data.r); // 更新UI逻辑... }; ``` 如果希望更灵活地管理请求生命周期或者使用现代浏览器特性,则可考虑基于 Fetch API 结合 ReadableStream 接口的方式来进行操作。这种方式不仅限于 SSE 协议,还可以适用于 WebSocket 或者其他类型的流传输协议。 下面是一段利用 Fetch API 结合 AbortController 控制取消请求的例子,同时也展示了如何读取响应体作为流逐块解析JSON格式的内容。 ```typescript async function handleStreamingResponse(url:string, reqBody:any){ try{ let abortController = new AbortController(); const response = await fetch(url, { signal: abortController.signal, method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({...reqBody}) }); if (!response.ok || !response.body) throw Error('Network response was not ok'); const reader = response.body.getReader(); const decoder = new TextDecoder(); while(true){ const {done,value}=await reader.read(); if(done)break; processChunk(decoder.decode(value)); } function processChunk(chunkString:string){ // 解析chunk字符串为json对象更新视图 try{ const parsedData=JSON.parse(chunkString.trim()); updateView(parsedData); // 自定义函数用于更新页面上的数据显示 } catch(error){ console.error("Failed to parse chunk:",error); } } }catch(e){ console.error("Error during streaming request:",e.message); } } ``` 此代码片段演示了当接收到一块新的数据时会调用 `processChunk()` 函数尝试将其转换成有效的 JSON 对象再进一步处理。需要注意的是由于网络延迟等原因可能导致多个独立的消息被合在一起作为一个单独的大块传递过来;因此建议设计服务端输出时确保每条记录之间有明显的分隔符以方便客户端正确拆分多条记录的情况。 另外,在实际项目中通常还需要考虑到错误重试机制、断线续传等功能需求,这取决于具体应用场景的要求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值