当前位置: 首页 > news >正文

前端接收流式数据demo,并用markdown解析数据,包括EventSource和fetch两种方式

AI模型对话,前端接受流式数据效果demo

可以直接复制代码

如上视频,要实现类似deepseek和chatgpt的效果,用传统的方式接受数据根本行不通,必须使用SSE, Server-Sent Events(SSE)功能,允许服务端推送数据到客户端,通常叫数据推送。当数据源有新数据,它马上发送到客户端,不需要再等待客户端请求。
这里提供了完整的前端代码和用node.js写的后端模拟数据,分别包括了get和post方式,因为EventSource只支持get方式,后用fetch支持了post方式,两种效果都是一样的。

前端用的vue2,node版本v16.20.2,介绍一篇nvm管理node版本的文章(https://blog.csdn.net/qq_41822157/article/details/141259132)

DeepSeekChatGet.vue

<template><div class="deepseek-chat"><div class="chat-container" ref="chatContainer"><div class="messages"><divv-for="(message1, index) in messages":key="index":class="['message', message1.role]"><div class="avatar"><span v-if="message1.role === 'user'"></span><span v-else>DS</span></div><div class="message-content"><divclass="message-body"v-for="(message, index1) in message1.arr":key="index1"><!-- 文本内容 --><div v-if="message1.role === 'user'" class="text-content"><p>{{ message.content }}</p></div><div v-else class="text-content"><p v-if="index === 0">{{ message.content }}</p><div v-else><div><pv-html="md.render(message.content)"class="chatDatas-box"></p></div></div></div><!-- 加载状态 --><divv-if="index1 === messages[messages.length - 1].arr.length - 1 &&isStreaming"class="typing-indicator"><span></span><span></span><span></span></div></div></div></div></div></div><div class="input-area"><div class="input-container"><textareav-model="userInput"placeholder="输入消息..."@keydown.enter.exact.prevent="sendMessage3"@keydown.shift.enter="newLine"rows="2"ref="textarea"></textarea><button @click="sendMessage3" :disabled="isStreaming"><span v-if="!isStreaming">发送</span><span v-else>生成中...</span></button></div></div></div>
</template><script>
class DeepSeekStream {constructor() {this.buffer = "";this.eventSource = null;}connect(url, onMessage, onComplete, onError, onStart) {this.eventSource = new EventSource(url);this.eventSource.addEventListener("message", (event) => {try {// 确保处理完整的消息if (event.data.trim()) {const message = JSON.parse(event.data);onMessage(message);}} catch (error) {onError(`消息解析错误: ${error.message}`);}});this.eventSource.addEventListener("end", () => {onComplete();this.close();});this.eventSource.addEventListener("error", (error) => {onError(`流错误: ${error.message}`);this.close();});this.eventSource.addEventListener("open", () => {//监听连接成功的时候onStart();});}close() {if (this.eventSource) {this.eventSource.close();this.eventSource = null;}}
}// 导入解析markdown语法的第三方库markdown-it
import MarkdownIt from "markdown-it";// 引入代码高亮
import hljs from "highlight.js";
// 你用到了什么语言就要引入什么语言,目前我还不知道怎么样导入所有语言
import javascript from "highlight.js/lib/languages/javascript";
//  这个是高亮的样式,有很多,我选了这个
import "highlight.js/styles/ir-black.css";hljs.registerLanguage("javascript", javascript);
import "highlight.js/styles/github-dark.css";
export default {name: "DeepSeekChat",data() {return {userInput: "js中 array.sort的用法",messages: [{role: "assistant",model: "deepseek-chat",arr: [{content: "您好!请输入您的问题",type: "text",},],},],isStreaming: false,streamHandler: null,selectedModel: "deepseek-chat",chatDatas: [],md: new MarkdownIt({html: true, // 允许HTML标签linkify: true, // 自动转换URL为链接typographer: true, // 优化排版breaks: true, // 将换行符转换为<br>highlight: function (str, lang) {// 代码高亮处理if (lang && hljs.getLanguage(lang)) {try {return ('<pre class="hljs"><code>' +hljs.highlight(lang, str, true).value +"</code></pre>");} catch (error) {console.error(error);}}return ('<pre class="hljs"><code>' +this.md.utils.escapeHtml(str) +"</code></pre>");},}),};},computed: {},methods: {sendMessage3() {if (!this.userInput.trim() || this.isStreaming) return;// 添加用户消息this.messages.push({role: "user",model: this.selectedModel,arr: [{content: this.userInput,},],});const prompt = this.userInput;// const prompt = "111";this.userInput = "";// 添加AI消息占位this.messages.push({role: "assistant",model: this.selectedModel,arr: [],});let arr1 = { content: "思考中,请稍候..." };this.messages[this.messages.length - 1].arr.push(arr1);// 创建流处理器this.streamHandler = new DeepSeekStream();const url = `http://localhost:3000/deepseek-stream?prompt=${encodeURIComponent(prompt)}&model=${this.selectedModel}`;console.log("this.messages", this.messages);this.streamHandler.connect(url,(data) => {// console.log("data--", data.content);arr1.content += data.content;// console.log("this.chatDatas---", this.chatDatas);// 滚动到底部this.scrollToBottom();},() => {console.log("结束了");this.isStreaming = false;this.scrollToBottom();},(error) => {console.error("流错误:", error);},() => {// console.log("开始---111");this.messages[this.messages.length - 1].arr[0].content = "";});this.scrollToBottom();},newLine() {this.userInput += "\n";this.$nextTick(() => {this.$refs.textarea.scrollTop = this.$refs.textarea.scrollHeight;});},copyCode(code) {navigator.clipboard.writeText(code).then(() => {// this.$toast.success("代码已复制到剪贴板");});},scrollToBottom() {this.$nextTick(() => {const container = this.$refs.chatContainer;container.scrollTop = container.scrollHeight;});},},mounted() {this.scrollToBottom();},beforeDestroy() {if (this.streamHandler) {this.streamHandler.close();}},
};
</script><style lang="less" scoped>
http://www.dtcms.com/a/270966.html

相关文章:

  • LeetCode 138题解 | 随机链表的复制
  • 力扣 hot100 Day39
  • 【保姆级喂饭教程】Windows下安装Git Flow
  • 电网的智能觉醒——人工智能重构能源生态的技术革命与公平悖论
  • JAVA策略模式demo【设计模式系列】
  • 自动化Trae Apollo参数解释的批量获取
  • 苍穹外卖项目日记(day04)
  • ASP.NET Core 8 轻松配置Serilog日志
  • 智慧码头船舶网络部署5G工业路由器无人值守场景应用
  • 无人设备遥控器之双向通讯技术篇
  • 【机器人】Aether 多任务世界模型 | 4D动态重建 | 视频预测 | 视觉规划
  • C++并发编程-11. C++ 原子操作和内存模型
  • Linux驱动学习day20(pinctrl子系统驱动大全)
  • Ubuntu防火墙缺失问题(unit firewalld.service could not be found, ubuntu 22)
  • EFK9.0.3 windows搭建
  • Linux系统管理实战:生成大文件与定位磁盘挂载点
  • 专题:2025母婴行业洞察报告|附60+份报告PDF汇总下载
  • Linux中shell(外壳)和内核(kernel)的关系
  • Claude Code:终端上的 AI 编码助手,潜力与挑战并存
  • 从零用java实现 小红书 springboot vue uniapp(13)模仿抖音视频切换
  • 华为数通HCIA vs HCIP:新手入门选哪个更合适?
  • 利用sCMOS科学相机测量激光散射强度
  • Rk3568驱动开发_阻塞IO_15
  • SQL Server通过存储过程实现飞书消息卡片推送
  • Live555-RTSP服务器
  • nl2sql的解药pipe syntax
  • 【工具变量】上市公司企业金融强监管数据、资管新规数据(2001-2024年)
  • 【YOLOv11-目标检测】目标检测数据格式(官方说明)
  • S7-200 SMART :通过以太网下载程序详细步骤
  • React、Vue、Angular的性能优化与源码解析概述