HTML Server-Sent 事件(SSE)教程:把“服务器推送”做成原生事件流
58
0
0
一句话:SSE 就是“服务器→浏览器”的单向直播流,基于纯 HTTP,自动重连,不用装插件,不用写轮询;适合实时股价、打字机 GPT、进度条等场景 。
1. 能做什么?场景先对齐
| 场景 | 推送内容 | 为啥用 SSE |
|---|---|---|
| 股票看板 | 最新价 | 低延迟、自动重连 |
| GPT 打字机 | 逐字增量 | 文本流天然匹配 |
| 进度条 | 0→100% | 一条长连接搞定 |
| 系统通知 | 公告/私信 | 后端广播超简单 |
2. 三步完成最小可运行示例
① 后端(Node/Express 为例)→ 返回 text/event-stream
// server.js
const express = require('express');
const app = express();
app.get('/sse', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*' // 开发阶段
});
let n = 0;
const timer = setInterval(() => {
res.write(`data: 现在是 ${new Date().toLocaleTimeString()}\n\n`);
if (++n > 10) { clearInterval(timer); res.end(); }
}, 1000);
});
app.listen(3000);每条消息以\n\n结尾;必须text/event-stream头 。
② 前端 → 原生 EventSource 即可
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>SSE 最小 Demo</title>
</head>
<body>
<ul id="log"></ul>
<script>
const log = document.getElementById('log');
const source = new EventSource('/sse'); // ① 建立长连
source.onmessage = (e) => { // ② 默认 message 事件
const li = document.createElement('li');
li.textContent = e.data;
log.appendChild(li);
};
source.onerror = (e) => { // ③ 断线/异常
console.error('SSE 断开', e);
};
</script>
</body>
</html>打开页面 → 每秒收到服务器时间 → 10 秒后自动结束。
3. 消息格式:一行指令 = 一个事件
event: stockUpdate ← 可选,自定义事件名
id: 123 ← 可选,断线续传依据
retry: 5000 ← 可选,重连间隔毫秒
data: {"symbol":"AAPL","price":148.50} ← 必须,可多行顺序无关,以 \n\n 结束;同一次响应可混多条 。4. 自定义事件:一条通道分多主题
前端监听命名事件:
const source = new EventSource('/stocks');
source.addEventListener('stockUpdate', (e) => {
const stock = JSON.parse(e.data);
console.log(stock.symbol, stock.price);
});
source.addEventListener('notice', (e) => {
showToast(e.data);
});后端对应输出:
res.write('event: stockUpdate\n');
res.write('data: {"symbol":"AAPL","price":148.50}\n\n');
res.write('event: notice\n');
res.write('data: 系统将于 15:00 升级\n\n');5. 自动重连 & Last-Event-ID:断网续传原生带
- 浏览器自动重连,默认 3 秒间隔;可用
retry: 2000改 。 - 重连时会带
Last-Event-ID头,服务端可据此续推:
const lastId = req.headers['last-event-id'];
if (lastId) { /* 从 lastId 之后继续推 */ }适合「行情断点续传」、「GPT 续打字」。
6. 与 WebSocket 的快选对照
| 维度 | SSE | WebSocket |
|---|---|---|
| 方向 | 单向(服务端→客户端) | 双向 |
| 协议 | 纯 HTTP,80/443 即可 | 需 Upgrade 到 ws/wss |
| 自动重连 | ✅ 原生 | ❌ 自己写 |
| 二进制 | ❌ 仅文本(可 base64) | ✅ 帧级二进制 |
| 开销 | 小,HTTP 头重复 | 低,帧头极小 |
| 兼容性 | IE 不支持(Edge 79+ 补票) | 现代浏览器全 OK |
结论:只要服务端推、文本数据 → 优先 SSE;双向/二进制 → 上 WebSocket。
7. 调试 & 性能 Tips
- DevTools → Network → 选中 SSE 请求 → 看持续更新的
Event Stream。 - Nginx 反向代理 加
proxy_buffering off;,否则事件被缓冲到 4KB 才吐。 - CORS 别忘
Access-Control-Allow-Origin& 允许Last-Event-ID头。 - 断线提示 可在
onerror里做“重连中…”浮层,成功后再隐藏。
8. 10 行代码实现「GPT 打字机」效果
前端:
const output = document.getElementById('output');
const source = new EventSource('/gpt?prompt=' + encodeURIComponent(prompt));
source.onmessage = (e) => {
if (e.data === '[DONE]') { source.close(); return; } // 结束标记
output.textContent += e.data; // 逐字追加
};后端(Node 伪代码):
for (const char of answer) {
res.write(`data: ${char}\n\n`);
await delay(50); // 50 ms 打字间隔
}
res.write('data: [DONE]\n\n');每字符 50 ms,用户体验≈ChatGPT 网页版。
9. 口诀总结(背完走天下)
“SSE 是 HTTP 长直播,
Content-Type 记得 text/event-stream;
data 结尾 nn,事件 ID 断点续;
EventSource 原生重连,双向场景换 WebSocket。”
下次后端说“我要推实时数据”,先想 SSE——轻量、原生、无依赖,10 分钟就能上线!
0
快来点个赞吧
发表评论
评论列表