不用第三方SDK!Vue3原生Fetch实现DeepSeek流式输出,90%前端都会踩的分片解析坑一次性解决
前言做AI聊天页面你一定遇过两种糟心体验关闭流式点击提交黑屏等待3-10秒一次性弹出全文用户等待感极强手写SSE流式网络分包截断JSON疯狂报JSON.parse解析失败文字丢失乱码网上很多示例只给极简demo没有处理分片容错上线必崩。本文基于ViteVue3原生Fetch完整实现DeepSeek对话同时支持流式打字机/一次性返回双模式自带buffer分片容错逻辑看完你能学到SSE流式输出底层二进制流传输原理ReadableStream、TextDecoder浏览器原生API完整用法buffer缓冲区解决TCP分包截断JSON的核心方案流式/非流式接口两套分支代码完整实现开发高频踩坑清单修复方案直接规避线上bug可直接复制运行的完整单文件组件一、先搞懂什么是LLM流式SSE输出1.1 传统一次性请求streamfalse后端等AI完整生成全部文本组装成完整JSON一次性返回。前端调用response.json()直接解析优点代码简单缺点等待时间长交互割裂。1.2 SSE流式请求streamtrue大模型每生成一段Token就封装成data: JSON格式通过二进制流实时推送到前端传输载体response.body二进制可读流Uint8Array字节数组分隔规则每条数据用换行\n分割结尾单独发送data: [DONE]标识流结束传输痛点TCP网络分包会把一条完整JSON拆成两半直接解析报错必须用buffer缓存残缺片段1.3 核心API介绍response.body.getReader()创建流读取器逐块拉取二进制数据TextDecoder()二进制Uint8Array转UTF-8字符串解决中文乱码buffer缓冲区存储上一轮未解析完成的残缺data:报文下一轮拼接完整再解析二、项目前置环境配置2.1 依赖无需额外安装本方案纯浏览器原生API不需要openai/langchain等第三方SDKVite Vue3项目开箱即用。2.2 环境变量配置关键防止密钥硬编码泄露项目根目录新建.env文件填入DeepSeek密钥VITE_DEEPSEEK_API_KEYsk-xxxxxxxxxxxxxxxxxxxxVite通过import.meta.env.VITE_XXX读取环境变量打包后不会明文暴露密钥。三、完整可运行代码 App.vuescript setup import { ref } from vue // 响应式状态 const question ref(讲一个中国龙的故事); // 用户输入提问 const content ref(); // AI输出内容 const stream ref(true); // 是否开启流式输出开关 // 核心请求函数 const update async () { // 空提问拦截避免无效请求 if (!question.value) return; content.value 思考中...; // DeepSeek对话接口地址 const endpoint https://api.deepseek.com/chat/completions; const headers { Content-Type: application/json, Authorization: Bearer ${import.meta.env.VITE_DEEPSEEK_API_KEY} }; // 发起POST请求 const response await fetch(endpoint, { method: POST, headers, body: JSON.stringify({ model: deepseek-v4-flash, messages: [ { role: user, content: question.value } ], stream: stream.value // 动态控制流式开关 }) }) // 分支1流式输出打字机效果本文核心 if (stream.value) { content.value ; // 清空思考中占位文字 // 获取二进制流读取器 const reader response.body?.getReader(); // 二进制转UTF8文本解码器 const decoder new TextDecoder(); let done false; // 流读取完成标记 let buffer ; // 残缺分片缓存解决JSON截断报错核心 // 循环持续拉取二进制分片 while (!done) { // 异步读取一块二进制数据 const { value, done: doneReading } await reader?.read(); done doneReading; // 拼接上一轮残留残缺片段 当前新解码文本 const chunkValue buffer decoder.decode(value); buffer ; // 缓存已合并清空等待下一轮残缺数据 // 按换行分割文本过滤仅保留data:开头的SSE有效行 const lines chunkValue.split(\n) .filter((line) line.startsWith(data: )) // 逐行解析每条SSE报文 for (const line of lines) { // 切掉前缀 data: 6个字符获取纯JSON/结束标识 const incoming line.slice(6); // 检测到结束标识终止全部循环 if (incoming [DONE]) { done true; break; } try { // 解析JSON字符串 const data JSON.parse(incoming); // 流式专属增量文本delta const delta data.choices[0].delta.content; // 存在增量文字则追加到页面实现打字机效果 if (data delta) { content.value delta; } } catch (err) { // JSON解析失败分片不完整存入buffer下一轮拼接 buffer data: ${incoming}; } } } } // 分支2非流式一次性返回 else { const data await response.json(); // 非流式使用message完整文本而非delta增量 content.value data.choices[0].message.content; } } /script template div classcontainer !-- 提问输入区域 -- div label输入/label input classinput v-modelquestion / button clickupdate提交/button /div !-- 流式开关 AI回答展示区 -- div classoutput div labelStreaming流式输出/label input typecheckbox v-modelstream / /div div{{ content }}/div /div /div /template style scoped .container { display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; height: 100vh; font-size: 0.85rem; padding: 20px; } .input { width: 300px; padding: 4px 8px; } .output { margin-top: 12px; min-height: 300px; width: 100%; text-align: left; line-height: 1.6; } button { padding: 4px 12px; margin-left: 8px; cursor: pointer; } /style四、核心流式逻辑逐行深度拆解4.1 基础变量初始化if(stream.value){content.value;constreaderresponse.body?.getReader();constdecodernewTextDecoder();letdonefalse;letbuffer;reader流专属读取器串行读取二进制数据保证顺序不乱decoder全局解码器循环内复用避免中文跨分片乱码done外层while循环开关控制数据流是否全部接收完毕buffer全文最关键容错变量专门存储被TCP分包截断的半条data:报文4.2 while循环持续拉取二进制分片while(!done){const{value,done:doneReading}awaitreader?.read();donedoneReading;constchunkValuebufferdecoder.decode(value);buffer;constlineschunkValue.split(\n).filter((line)line.startsWith(data: ))}reader.read()异步阻塞读取有新分片立刻返回无数据持续等待chunkValue buffer 新文本核心容错操作把上一轮残缺片段和本次新数据拼接保证报文完整split(\n)SSE协议每条数据换行分隔切割后过滤无效空行、心跳包只保留data:有效数据4.3 for循环解析单条SSE报文for(constlineoflines){constincomingline.slice(6);if(incoming[DONE]){donetrue;break;}try{constdataJSON.parse(incoming);constdeltadata.choices[0].delta.content;if(datadelta)content.valuedelta;}catch(err){bufferdata:${incoming};}}line.slice(6)剔除data:固定前缀提取纯JSON字符串[DONE]服务端流结束标志终止所有循环delta.content流式接口专属增量字段每次仅返回本次生成的少量文字Vue响应式追加实现逐字打字效果catch容错逻辑JSON解析报错代表当前行是残缺报文存入buffer下一轮循环拼接新分片后再解析杜绝文字丢失4.4 非流式分支简单说明else{constdataawaitresponse.json();content.valuedata.choices[0].message.content;}关闭流式时后端等待AI全部生成完毕一次性返回完整JSON使用message.content完整文本无需处理二进制流、分片、buffer代码极简但用户等待体验差。五、高频开发踩坑清单必看上线避坑坑1TCP分包截断JSON疯狂报parse错误现象控制台频繁抛出JSON语法错误AI回答文字残缺、丢失原因网络传输会把一条data: JSON切成两块单块无法完整解析解决方案代码中buffer缓冲区拼接残缺片段后再解析坑2中文跨分片解码出现乱码现象部分中文显示问号、乱码字符优化方案decoder.decode(value, { stream: true })解码器自动缓存跨分片字节完整解析中文坑3忘记清空buffer重复叠加文本现象AI回答重复、内容翻倍修复拼接chunkValue后立刻执行buffer 清空缓存坑4混淆流式/非流式字段 delta / message现象关闭流式返回undefined开启流式无文字输出区分streamtrue →data.choices[0].delta.contentstreamfalse →data.choices[0].message.content坑5连续点击提交多请求文字叠加错乱优化补充增加loading锁请求期间禁用提交按钮防止并发请求坑6API Key硬编码写在代码内风险前端打包后源码泄露密钥产生高额扣费规范统一放入.env环境变量通过import.meta.env读取六、流式与非流式方案对比对比维度streamtrue 流式SSEstreamfalse 一次性返回传输方式二进制分片持续推送完整JSON单次返回解析逻辑ReadableStreambuffer容错直接response.json()输出字段delta.content增量小段message.content全文用户体验边生成边展示低等待感知等待全部生成后一次性渲染代码复杂度高需处理分片、异常截断极低两行代码完成适用场景正式AI对话产品内部简单工具、本地Demo七、项目扩展优化方向增加加载锁新增loading响应式变量请求中禁用按钮防止重复点击异常捕获外层增加try/catch处理网络失败、401密钥错误、接口限流Markdown渲染流式输出纯文本流结束后引入marked渲染富文本多轮对话扩展messages数组存储历史聊天上下文实现连续对话中断请求使用AbortController支持中途停止AI生成换行样式兼容CSS增加white-space: pre-wrap保留AI返回换行格式八、总结AI产品丝滑打字机交互核心依靠SSE流式输出原生FetchReadableStream无需第三方SDK即可实现buffer缓冲区是流式解析的灵魂专门解决TCP分包截断JSON的线上致命bugDeepSeek接口区分流式/非流式两套返回结构delta与message字段切勿混用生产环境优先使用流式输出提升用户体验同时做好分片容错、异常捕获、密钥安全管理。

相关新闻

如何让微信聊天记录真正属于你?5步实现数据自主管理

如何让微信聊天记录真正属于你?5步实现数据自主管理

如何让微信聊天记录真正属于你?5步实现数据自主管理 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeChatM…

2026/8/5 11:39:13 阅读更多 →
如何快速掌握ComfyUI-VideoHelperSuite:AI视频处理的完整指南

如何快速掌握ComfyUI-VideoHelperSuite:AI视频处理的完整指南

如何快速掌握ComfyUI-VideoHelperSuite:AI视频处理的完整指南 【免费下载链接】ComfyUI-VideoHelperSuite Nodes related to video workflows 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-VideoHelperSuite 你是否曾为AI生成的精美图像序列无法变成…

2026/8/5 11:38:13 阅读更多 →
免费开源LCU自动化工具:League Toolkit终极使用指南

免费开源LCU自动化工具:League Toolkit终极使用指南

免费开源LCU自动化工具:League Toolkit终极使用指南 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power 🚀. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit League Toolkit是一款基于英雄…

2026/8/5 11:38:13 阅读更多 →

最新新闻

React Native 移动端取证应用开发:元数据固化与双模式协同设计

React Native 移动端取证应用开发:元数据固化与双模式协同设计

1. 项目缘起:一个快递员的“取证”刚需 去年双十一,我帮一个开驿站的朋友处理了几单棘手的包裹纠纷。客户坚称收到的手机屏幕是碎的,而驿站这边的出库监控又恰好有个死角,拍不清包裹的最终状态。最后扯皮了半个月,朋友…

2026/8/5 12:24:31 阅读更多 →
FastLED电源管理终极指南:如何精确计算LED电流与设计可靠供电系统

FastLED电源管理终极指南:如何精确计算LED电流与设计可靠供电系统

FastLED电源管理终极指南:如何精确计算LED电流与设计可靠供电系统 【免费下载链接】FastLED The FastLED library for colored LED animation on Arduino. Please direct questions/requests for help to the FastLED Reddit community: http://fastled.io/r Wed li…

2026/8/5 12:24:31 阅读更多 →
5分钟拯救杂乱Windows桌面:NoFences免费分区工具完整指南

5分钟拯救杂乱Windows桌面:NoFences免费分区工具完整指南

5分钟拯救杂乱Windows桌面:NoFences免费分区工具完整指南 【免费下载链接】NoFences 🚧 Open Source Stardock Fences alternative 项目地址: https://gitcode.com/gh_mirrors/no/NoFences 你是否每天都要在混乱的桌面图标海洋中寻找需要的文件&a…

2026/8/5 12:24:31 阅读更多 →
CTF 选手收藏:50 个实战解题思路,一篇全部讲透

CTF 选手收藏:50 个实战解题思路,一篇全部讲透

CTF 选手收藏:50 个实战解题思路,一篇全部讲透 CTF竞赛的核心逻辑 • 核心目标:快速拆解问题(Flag导向)、工具链协作、模式化思维。• 关键原则:先广度后深度(优先收集信息)、分治策…

2026/8/5 12:24:31 阅读更多 →
Linux gnome设置触控板当连接鼠标时关闭

Linux gnome设置触控板当连接鼠标时关闭

前言 注:本文以gnome桌面环境演示 笔记本电脑自带一个触控板,但平常都用鼠标,触控板会造成误触,所以一般在设置中关闭触控板,但有时没有鼠标的情况下会用触控板应急。 Windows上有个"当连接鼠标时关闭触控板"…

2026/8/5 12:24:31 阅读更多 →
电路分析中的源变换:从戴维南定理到工程实践

电路分析中的源变换:从戴维南定理到工程实践

1. 从“黑盒”到“白盒”:为什么我们需要源变换 在电路分析的世界里,我们常常会遇到一些“难啃的骨头”——那些看起来结构复杂、元件繁多,让你一眼望去就头皮发麻的电路网络。无论是学生时代面对课后习题,还是工程师在调试一块新…

2026/8/5 12:23:31 阅读更多 →

日新闻

Java缓存框架:JetCache

Java缓存框架:JetCache

TOC 一、简介 JetCache 是一个 Java 缓存抽象框架,为不同的缓存解决方案提供了统一的使用方式。 它提供的注解比 Spring Cache 更加强大。 JetCache 的注解支持原生 TTL、两级缓存以及在分布式环境中的自动刷新功能,同时你也可以通过代码直接操作 Cach…

2026/8/5 0:00:43 阅读更多 →
AD 铺铜设置十字连接,过孔全连接,新版AD的简单设置

AD 铺铜设置十字连接,过孔全连接,新版AD的简单设置

需求:通孔焊盘 十字花;过孔 Via 实心直连;贴片焊盘按需设置 AD 测试版本AD24 很多工程师踩坑:全部统一十字,导致接地过孔阻抗高、大电流发热! 一、快捷键打开规则 PCB 界面按下:D R 展开…

2026/8/5 0:00:43 阅读更多 →
AI素描转换技术深度拆解(2024最新论文+工业级落地代码):从Stable Diffusion ControlNet到LoRA微调全链路解析

AI素描转换技术深度拆解(2024最新论文+工业级落地代码):从Stable Diffusion ControlNet到LoRA微调全链路解析

更多请点击: https://kaifayun.com 第一章:AI生成素描效果 AI生成素描效果是计算机视觉与风格迁移技术融合的典型应用,其核心在于将彩色照片或RGB图像转换为具有手绘质感、明暗对比强烈、边缘清晰的单色素描图像。该过程通常依赖于深度学习模…

2026/8/5 0:00:43 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/4 13:24:41 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/4 11:41:39 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/5 10:20:36 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/4 13:38:24 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/4 11:09:16 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/4 13:38:40 阅读更多 →