前端学习笔记(18)处理sse流式输出 笔记【前端处理sse流式输出】2026年6月9日 22:34什么是sse协议一种服务器向客户端单项推送内容的技术服务器连续的向客户端发送数据每次发送一段纯文本核心的格式规范以标识【data:】开头以【\n\n】两个换行符结尾data:【一次完整的数据内容】\n\nsse还支持以下标识事件【event:】消息id【id:】重连间隔【retry:】dify的sse规范dify基本只发送data标识data的数据内容是json的格式这个json固定有以下属性event数据发送的事件conversation_id数据发送的对话id。下次向dify请求时候带这个就能接上这次对话message_id每次请求的消息iddata除了message事件其他event在这里传关键数据每种event有固定的data格式answermessage事件特有的属性每次传一点LLM流式输出的文本片段dify的sse事件工作流启动event为workflow_started节点开始event为node_started节点结束event为node_finished消息节点event为messagejs接dify的sse返回项目自己封装的$fetch, 是基于axios的用不了0.18.0版本为什么不能用axios这里不能用axios因为axios是对xhr的封装xhr需要等到一次响应完毕才能一次性读到res.data需要用fetch()fetch返回一个浏览器原生response对象res.body是一个ReadableStream实例用这个可以一段一段的获取数据实现方法通过一个异步方法sendDifyMessage向dify发送请求返回值的answer随时间不断变动逐步拼接messageasyncsendDifyMessage({query,conversation_id,files,onMessage}){constparams。。。// 按dify的要求构建参数// 有一个地方要注意params.body.response_mode需要是streamingconstresasyncfetch(DIFY_URL,params)constreaderres.body.getReader()constdecodernewTextDecoder()// 可以使用response.body.getReader().read()异步方法获取流的下一块数据// const {done,chunk1} async reader.read()// const {done,chunk2} async reader.read()// const {done,chunk3} async reader.read()// const {done,chunk4} async reader.read()// 循环获取数据直到dify的数据全部发送完成// 同时每个chunk的格式是Uint8Array需要先解析再拼起来用TextDecoder()解析letbufferletanswer// 不断的刷新数据while(true){const{done,chunk}asyncreader.read()if(done)break//流停止就结束// stream: true避免中途阶段bufferdecoder.decode(value,{stream:true})// 去掉不完整的最后一行// 如果完整传回一行最后必定是\n\npop()去掉的只会是一个空串constlinesbuffer.split(\n)bufferlines.pop()||// 这里的line是一行【标识:数据】for(constlineoflines){// dify的sse只有data需要处理if(!line.startsWith(data: ))continuetry{// 去掉【data: 】剩下的是一个JSON字符串consteventJSON.parse(line.slice(6))// 按if(event.eventmessage){answerevent.answer||if(event.conversation_id)conversation_idevent.conversation_idif(onMessage)onMessage({type:chunk,answer:event.answer,fullAnswer:answer})}elseif(event.eventmessage_end){if(event.conversation_id)conversation_idevent.conversation_idif(onMessage)onMessage({type:end,fullAnswer:answer})}elseif(event.eventerror){if(onMessage)onMessage({type:error,message:event.message})}}catch(e){// 非 JSON 行跳过}}}