之前已经发过简单使用get请求接口EventStream数据:
vue处理接口返回EventStream数据并进行展示
使用post请求接口EventStream数据具体操作如下:
使用sse.js第三方插件实现:
安装sse.js:
javascript
npm install sse
vue页面代码:
javascript
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文件内容:
javascript
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)
}