Loading...

文章背景图

如何使用 SSE 做轻量实时推送

2026-08-10
43
-
- 分钟
|

在前端开发里,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

AI 流式输出

SSE 或 fetch stream

任务进度

SSE

通知中心

SSE

聊天室双向通信

WebSocket

实时协作编辑

WebSocket

游戏、低延迟双向交互

WebSocket

SSE 的优点是简单、基于 HTTP、浏览器原生支持、自动重连。缺点是只能服务端推客户端,并且原生 EventSource 对请求方法和 header 的控制较弱。

10. 常见注意点

  1. 服务端必须设置:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
  1. 每条消息必须用两个换行符结束:

data: hello
  1. 连接结束后前端应调用:

source.close();
  1. 生产环境中要注意代理配置,比如 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();

评论交流

文章目录