1. 为什么SSE是流式聊天的“理想搭档”?
如果你最近在捣鼓AI聊天应用,尤其是想实现那种像真人打字一样、一个字一个字往外蹦的效果,那你肯定绕不开“流式输出”这个概念。传统的API请求是“一问一答”,你发个问题过去,后端吭哧吭哧算半天,最后把一整段完整的答案打包扔回来。用户只能盯着空白页面干等,体验非常割裂。而流式输出就像拧开了水龙头,数据是“涓涓细流”,持续不断地从服务器流向客户端,用户可以立刻看到生成过程,体验瞬间就流畅、自然了。
要实现这种效果,技术上主要有两大流派:WebSocket 和 Server-Sent Events (SSE)。很多新手会纠结选哪个,我刚开始也一样。简单来说,WebSocket是双向通信的“高速公路”,功能强大,适合在线游戏、实时协作这种需要频繁双向交互的场景。但它的“收费站”(服务器连接开销)和“施工复杂度”(协议升级、心跳维护、重连逻辑)也更高。
而SSE,更像是服务器到客户端的“单向广播”。它基于最普通的HTTP协议,服务器可以主动推送消息,客户端只管接收就行。对于GPT流式聊天这种典型的“你问我答,我一边想一边说”的场景,SSE简直是量身定做。它的优势太明显了:实现简单(就是个特殊的HTTP响应),天然支持自动重连(连接断了浏览器自己会尝试重连),开销极低。我实测过,在只需要服务器推送的场景下,用SSE比用WebSocket,服务端的连接资源占用能少一大截,前端代码也清爽得多。
那么,在前端怎么接住SSE这股“数据流”呢?你可能会想到用原生的 EventSource API。没错,它能用,但我踩过坑。EventSource 有个很大的限制:它只支持GET请求,而且请求头是固定的,你没法自定义 Authorization 这种携带令牌的Header。在现代前后端分离、需要鉴权的应用里,这基本就是个“废柴”。所以,我们需要一个更强大的工具,这就是今天的主角—— @microsoft/fetch-event-source 库。它用Fetch API重新实现了SSE客户端,完美解决了 EventSource 的所有短板,让你既能享受SSE的轻便,又能拥有Fetch的全部灵活性。
2. 手把手入门:从零开始使用 @microsoft/fetch-event-source
说干就干,我们先把这个库用起来。安装过程毫无波澜,和你用过的任何前端库一样:
npm install @microsoft/fetch-event-source
# 或者
yarn add @microsoft/fetch-event-source
# 或者
pnpm add @microsoft/fetch-event-source
安装好后,我们来写一个最基础的请求。假设你的后端提供了一个SSE流式接口 /api/chat/stream。下面这段代码展示了如何发起请求并接收数据:
import { fetchEventSource } from '@microsoft/fetch-event-source';
async function startStreamingChat(query) {
// 定义一个控制器,用于在需要时主动中断流
const controller = new AbortController();
await fetchEventSource('/api/chat/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 看这里!可以轻松添加认证头,这是原生EventSource做不到的
'Authorization': `Bearer ${yourAuthToken}`,
},
body: JSON.stringify({
message: query,
// 你可以传递任何需要的参数
temperature: 0.7,
max_tokens: 1000,
}),
// 传入AbortSignal,用于控制请求中断
signal: controller.signal,
// 核心:当收到服务器发送的一个事件时触发
onmessage(event) {
// event.data 就是服务器推送过来的数据片段
console.log('收到数据块:', event.data);
// 通常,数据是JSON字符串,我们需要解析
try {
const parsedData = JSON.parse(event.data);
// 假设后端返回 { content: “生成的文字”, done: false }
if (parsedData.content) {
// 更新UI,将内容追加到聊天窗口
appendMessageToUI(parsedData.content);
}
} catch (e) {
// 如果不是JSON,可能是纯文本或结束信号
console.log('原始数据:', event.data);
}
},
// 连接成功打开时触发
onopen(response) {
console.log('连接已建立,状态码:', response.status);
// 你可以在这里处理响应头等信息
},
// 连接被服务器关闭时触发
onclose() {
console.log('服务器关闭了连接');
// 可以在这里做一些清理工作,或者提示用户连接结束
},
// 发生错误时触发(包括网络错误、解析错误等)
onerror(err) {
console.error('流式请求发生错误:', err);


522

被折叠的 条评论
为什么被折叠?



