悟夕导航

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 的快选对照

维度SSEWebSocket
方向单向(服务端→客户端)双向
协议纯 HTTP,80/443 即可需 Upgrade 到 ws/wss
自动重连✅ 原生❌ 自己写
二进制❌ 仅文本(可 base64)✅ 帧级二进制
开销小,HTTP 头重复低,帧头极小
兼容性IE 不支持(Edge 79+ 补票)现代浏览器全 OK
结论:只要服务端推文本数据 → 优先 SSE;双向/二进制 → 上 WebSocket。

7. 调试 & 性能 Tips

  1. DevTools → Network → 选中 SSE 请求 → 看持续更新的 Event Stream
  2. Nginx 反向代理proxy_buffering off;,否则事件被缓冲到 4KB 才吐。
  3. CORS 别忘 Access-Control-Allow-Origin & 允许 Last-Event-ID 头。
  4. 断线提示 可在 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
快来点个赞吧

发表评论

隐私评论

评论列表

来写一个评论吧