在前端开发里,event-stream 通常指服务端通过 Content-Type: text/event-stream 向浏览器持续推送事件。浏览器端使用 EventSource 接收数据,这套机制叫 SSE:Server-Sent Events。
它适合服务端单向推送,比如:
AI 流式输出
消息通知
任务进度
日志实时刷新
股票、订单、状态变更推送
和 WebSocket 不同,SSE 是 服务端到客户端的单向通道。如果前端只需要“接收服务端不断发来的数据”,SSE 通常比 WebSocket 更简单。
1. 最简单的前端用法
前端只需要创建一个 EventSource:
const source = new EventSource('/api/events');
source.onmessage = (event) => {
console.log('收到消息:', event.data);
};
source.onerror = (error) => {
console.error('连接异常:', error);
};服务端接口 /api/events 需要返回 text/event-stream 格式的数据。
浏览器收到的每一条消息,都会触发 onmessage。
2. 服务端返回的数据格式
SSE 的数据格式非常简单:
data: hello world
data: {"name":"Alice","age":18}
注意:每条消息之间要用 两个换行符 分隔。
如果发送 JSON,前端需要自己解析:
source.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(data.name);
};3. Node.js 示例
下面是一个 Express 服务端示例:
import express from 'express';
const app = express();
app.get('/api/events', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.write('data: connected\n\n');
let count = 0;
const timer = setInterval(() => {
count += 1;
const payload = {
count,
time: new Date().toISOString()
};
res.write(`data: ${JSON.stringify(payload)}\n\n`);
}, 1000);
req.on('close', () => {
clearInterval(timer);
res.end();
});
});
app.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});前端接收:
const source = new EventSource('http://localhost:3000/api/events');
source.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('当前计数:', data.count, data.time);
};4. 自定义事件类型
除了默认的 message 事件,SSE 还支持自定义事件名。
服务端发送:
event: progress
data: {"percent":60}
event: done
data: {"success":true}
前端监听:
const source = new EventSource('/api/events');
source.addEventListener('progress', (event) => {
const data = JSON.parse(event.data);
console.log('进度:', data.percent);
});
source.addEventListener('done', (event) => {
const data = JSON.parse(event.data);
console.log('完成:', data.success);
source.close();
});这种方式很适合处理任务进度、AI 输出完成状态等场景。
5. AI 流式输出示例
假设服务端逐段返回 AI 生成内容:
event: chunk
data: 你好,
event: chunk
data: 这是一个
event: chunk
data: 流式输出示例。
event: done
data: [DONE]
前端可以这样拼接内容:
let content = '';
const source = new EventSource('/api/chat-stream');
source.addEventListener('chunk', (event) => {
content += event.data;
document.querySelector('#output').textContent = content;
});
source.addEventListener('done', () => {
source.close();
console.log('输出完成');
});HTML:
<div id="output"></div>这就是很多 AI 聊天产品“逐字输出”的基础实现方式之一。
6. 自动重连机制
EventSource 默认支持自动重连。
如果连接中断,浏览器会尝试重新连接。服务端也可以指定重连间隔:
retry: 3000
data: reconnect after 3 seconds
表示断线后大约 3 秒重连。
前端可以监听错误:
source.onerror = () => {
console.log('连接断开,浏览器会尝试自动重连');
};如果你不想继续重连,需要手动关闭:
source.close();7. 传参方式
EventSource 默认只支持 GET 请求,所以常见传参方式是 query string:
const userId = '123';
const source = new EventSource(`/api/events?userId=${userId}`);如果需要带 cookie,可以这样:
const source = new EventSource('/api/events', {
withCredentials: true
});不过 EventSource 不能像 fetch 那样灵活设置自定义请求头。如果你需要传 Authorization: Bearer xxx,常见方案有:
使用 cookie 鉴权
使用短期 token 放在 URL 参数里
改用
fetch+ReadableStream改用 WebSocket
8. fetch + stream 替代方案
如果你需要 POST、自定义 header、请求体,就可以不用原生 EventSource,改用 fetch 读取流。
const response = await fetch('/api/chat-stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
prompt: '写一首短诗'
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
while (true) {
const { value, done } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
console.log('收到片段:', chunk);
}这种方式更灵活,但你需要自己处理数据切分、重连、事件格式等逻辑。
9. SSE 和 WebSocket 怎么选?
简单判断:
SSE 的优点是简单、基于 HTTP、浏览器原生支持、自动重连。缺点是只能服务端推客户端,并且原生 EventSource 对请求方法和 header 的控制较弱。
10. 常见注意点
服务端必须设置:
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive每条消息必须用两个换行符结束:
data: hello
连接结束后前端应调用:
source.close();生产环境中要注意代理配置,比如 Nginx 不能缓存或缓冲 SSE 响应。
Nginx 常见配置:
location /api/events {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_buffering off;
proxy_cache off;
}总结
event-stream / SSE 是一种非常适合前端实时接收服务端消息的方案。它比 WebSocket 更轻量,使用方式也更接近普通 HTTP 接口。
如果你的需求是:
服务端持续返回数据
前端只负责接收
不需要复杂双向通信
那么 SSE 通常是一个非常合适的选择。
典型前端代码其实就三步:
const source = new EventSource('/api/events');
source.onmessage = (event) => {
console.log(event.data);
};
source.close();