前端接流三大主流方式后端返回的是text/event-stream也就是 Server-Sent Events (SSE)。前端接收它主要有三种姿势方式1浏览器原生EventSource—— 最简单但功能有限方式2fetch API 流式读取—— 更灵活可自定义请求头方式3第三方库 microsoft/fetch-event-source—— 全能选手自动重连 方式1EventSource —— 杀鸡用牛刀够用就行如果你不需要自定义请求头比如不需要带TokenEventSource是最快的接入方式。几行代码搞定const eventSource new EventSource(http://localhost:8000/chat?prompt你好); eventSource.onmessage (event) { if (event.data [DONE]) { console.log(对话结束); eventSource.close(); return; } // 把内容追加到页面 document.getElementById(output).innerText event.data; }; eventSource.onerror (err) { console.error(连接出错, err); eventSource.close(); };注意EventSource只能发送GET请求且不能添加自定义Headers比如Authorization。如果你需要POST携带复杂参数就得用下面两种。 方式2fetch 流式读取 —— 真正的全栈式控制fetch API 从 Chrome 95 开始完美支持流式响应。我们可以像读小说一样逐段读取数据async function fetchStream() { const response await fetch(http://localhost:8000/chat, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ prompt: 讲个故事 }) }); const reader response.body.getReader(); const decoder new TextDecoder(); while (true) { const { done, value } await reader.read(); if (done) break; const chunk decoder.decode(value); // 假设后端每块返回 data: xxx\n\n 格式需要解析 const lines chunk.split(\n); for (const line of lines) { if (line.startsWith(data: )) { const data line.slice(6); if (data [DONE]) return; document.getElementById(output).innerText data; } } } } fetchStream();这种方式自由度超高可以加Token可以POST甚至可以处理二进制流。但要自己手动解析SSE格式容易在换行符上踩坑。️ 方式3microsoft/fetch-event-source —— 躺平式接入微软出品专治各种SSE不服。它内置了断线重连、自动解析、错误恢复等功能强烈推荐在生产环境使用import { fetchEventSource } from microsoft/fetch-event-source; await fetchEventSource(http://localhost:8000/chat, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ prompt: 写首诗 }), onmessage(ev) { if (ev.data [DONE]) { console.log(完成); return; } document.getElementById(output).innerText ev.data; }, onerror(err) { console.error(大事不妙, err); }, onclose() { console.log(连接关闭); } });它会自动处理SSE格式只要关注onmessage里的ev.data即可。连[DONE]都得自己判断它只管传数据。⚠️ 前方高能前端最容易翻车的五个坑坑1CORS 跨域 —— 万恶之源后端就算配了CORS如果前端用了withCredentials或自定义Header依然可能触发预检请求OPTIONS后端必须处理。用fetch-event-source时它默认不会发credentials注意设置。坑2数据格式必须严格遵循 SSE 规范后端每块必须是data: xxx\n\n两个换行符不能少。否则EventSource和微软库都可能解析失败。fetch方式自己解析也要注意换行可能跨块。坑3连接意外断开与重连机制EventSource 默认会自动重连但如果是HTTP 401/500等错误它会一直重连直到地老天荒。最好监听onerror判断状态码必要时关闭。微软库提供了onerror回调可以控制是否终止重连。坑4浏览器兼容性EventSource 和 fetch 的流式读取在 IE 和部分旧手机浏览器上不可用。可以用微软库的 polyfill或者提示用户升级浏览器。坑5内存泄漏与连接未关闭组件卸载或页面跳转时一定要调用eventSource.close()或中止fetch的AbortController否则连接会一直挂起浪费资源。 进阶小贴士让打字机体验更逼真前端显示时可以用定时器稍微打散一下文字出现节奏模拟真人打字。但注意不要过度否则用户会疯let buffer ; onmessage(ev) { buffer ev.data; // 每50ms渲染一个字符 if (!typingTimer) { typingTimer setInterval(() { if (buffer.length 0) { output.innerText buffer[0]; buffer buffer.slice(1); } else { clearInterval(typingTimer); typingTimer null; } }, 50); } }另外记得在UI上给个“停止生成”的按钮调用abort()或close()让用户随时打断。
如何让AI“打字机”效果在浏览器里丝滑跑起来?
前端接流三大主流方式后端返回的是text/event-stream也就是 Server-Sent Events (SSE)。前端接收它主要有三种姿势方式1浏览器原生EventSource—— 最简单但功能有限方式2fetch API 流式读取—— 更灵活可自定义请求头方式3第三方库 microsoft/fetch-event-source—— 全能选手自动重连 方式1EventSource —— 杀鸡用牛刀够用就行如果你不需要自定义请求头比如不需要带TokenEventSource是最快的接入方式。几行代码搞定const eventSource new EventSource(http://localhost:8000/chat?prompt你好); eventSource.onmessage (event) { if (event.data [DONE]) { console.log(对话结束); eventSource.close(); return; } // 把内容追加到页面 document.getElementById(output).innerText event.data; }; eventSource.onerror (err) { console.error(连接出错, err); eventSource.close(); };注意EventSource只能发送GET请求且不能添加自定义Headers比如Authorization。如果你需要POST携带复杂参数就得用下面两种。 方式2fetch 流式读取 —— 真正的全栈式控制fetch API 从 Chrome 95 开始完美支持流式响应。我们可以像读小说一样逐段读取数据async function fetchStream() { const response await fetch(http://localhost:8000/chat, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ prompt: 讲个故事 }) }); const reader response.body.getReader(); const decoder new TextDecoder(); while (true) { const { done, value } await reader.read(); if (done) break; const chunk decoder.decode(value); // 假设后端每块返回 data: xxx\n\n 格式需要解析 const lines chunk.split(\n); for (const line of lines) { if (line.startsWith(data: )) { const data line.slice(6); if (data [DONE]) return; document.getElementById(output).innerText data; } } } } fetchStream();这种方式自由度超高可以加Token可以POST甚至可以处理二进制流。但要自己手动解析SSE格式容易在换行符上踩坑。️ 方式3microsoft/fetch-event-source —— 躺平式接入微软出品专治各种SSE不服。它内置了断线重连、自动解析、错误恢复等功能强烈推荐在生产环境使用import { fetchEventSource } from microsoft/fetch-event-source; await fetchEventSource(http://localhost:8000/chat, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ prompt: 写首诗 }), onmessage(ev) { if (ev.data [DONE]) { console.log(完成); return; } document.getElementById(output).innerText ev.data; }, onerror(err) { console.error(大事不妙, err); }, onclose() { console.log(连接关闭); } });它会自动处理SSE格式只要关注onmessage里的ev.data即可。连[DONE]都得自己判断它只管传数据。⚠️ 前方高能前端最容易翻车的五个坑坑1CORS 跨域 —— 万恶之源后端就算配了CORS如果前端用了withCredentials或自定义Header依然可能触发预检请求OPTIONS后端必须处理。用fetch-event-source时它默认不会发credentials注意设置。坑2数据格式必须严格遵循 SSE 规范后端每块必须是data: xxx\n\n两个换行符不能少。否则EventSource和微软库都可能解析失败。fetch方式自己解析也要注意换行可能跨块。坑3连接意外断开与重连机制EventSource 默认会自动重连但如果是HTTP 401/500等错误它会一直重连直到地老天荒。最好监听onerror判断状态码必要时关闭。微软库提供了onerror回调可以控制是否终止重连。坑4浏览器兼容性EventSource 和 fetch 的流式读取在 IE 和部分旧手机浏览器上不可用。可以用微软库的 polyfill或者提示用户升级浏览器。坑5内存泄漏与连接未关闭组件卸载或页面跳转时一定要调用eventSource.close()或中止fetch的AbortController否则连接会一直挂起浪费资源。 进阶小贴士让打字机体验更逼真前端显示时可以用定时器稍微打散一下文字出现节奏模拟真人打字。但注意不要过度否则用户会疯let buffer ; onmessage(ev) { buffer ev.data; // 每50ms渲染一个字符 if (!typingTimer) { typingTimer setInterval(() { if (buffer.length 0) { output.innerText buffer[0]; buffer buffer.slice(1); } else { clearInterval(typingTimer); typingTimer null; } }, 50); } }另外记得在UI上给个“停止生成”的按钮调用abort()或close()让用户随时打断。