前端 Server-Sent Events (SSE) 是一种允许服务器向客户端单向推送更新的技术。与 WebSocket 相比,SSE 更加轻量,基于 HTTP 协议,并且专注于服务器到客户端的数据流。它非常适合那些只需要从服务器接收实时更新的场景,例如新闻推送、股票行情、体育赛事比分、进度条更新等。
SSE 的基本使用
SSE 主要通过浏览器内置的 EventSource
API 来实现。
-
创建
EventSource
实例:jsconst eventSource = new EventSource('http://localhost:3000/events'); // 如果是跨域请求,服务器需要设置 CORS 头 // const eventSource = new EventSource('http://api.example.com/stream');
EventSource
构造函数接收一个 URL 参数,指向服务器端提供 SSE 流的接口。
-
事件监听:
EventSource
实例会触发以下事件:onopen
:连接成功建立时触发。onmessage
:接收到服务器发送的无类型 消息时触发(即服务器端没有指定event:
字段的消息)。event.data
包含接收到的数据。onerror
:发生错误时触发(例如连接中断、网络错误等)。- 自定义事件:如果服务器发送了带有
event:
字段的消息,你可以通过addEventListener()
监听该自定义事件。
-
关闭连接:
使用
close()
方法关闭 SSE 连接。jseventSource.close();
基本使用示例(前端):
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSE Basic Demo</title>
</head>
<body>
<h1>Server-Sent Events 演示</h1>
<div id="messages"></div>
<button id="closeButton">关闭连接</button>
<script>
const messagesDiv = document.getElementById('messages');
const closeButton = document.getElementById('closeButton');
// 假设你的 SSE 服务器运行在 http://localhost:3000/events
// 你需要一个后端服务器来提供 SSE 服务
const eventSource = new EventSource('http://localhost:3000/events');
eventSource.onopen = (event) => {
console.log('SSE 连接已建立!', event);
messagesDiv.innerHTML += '<p style="color: green;">连接成功!</p>';
};
// 监听默认的 'message' 事件 (服务器只发送 data: 字段)
eventSource.onmessage = (event) => {
console.log('收到默认消息:', event.data);
messagesDiv.innerHTML += `<p><strong>默认消息:</strong> ${event.data}</p>`;
};
// 监听自定义事件 'stock_update' (服务器发送 event:stock_update)
eventSource.addEventListener('stock_update', (event) => {
console.log('收到股票更新:', event.data);
const stockData = JSON.parse(event.data); // 解析 JSON 字符串
messagesDiv.innerHTML += `<p style="color: blue;"><strong>股票更新 (${stockData.symbol}):</strong> ${stockData.price}</p>`;
});
// 监听自定义事件 'news_feed'
eventSource.addEventListener('news_feed', (event) => {
console.log('收到新闻推送:', event.data);
messagesDiv.innerHTML += `<p style="color: purple;"><strong>新闻:</strong> ${event.data}</p>`;
});
eventSource.onerror = (event) => {
console.error('SSE 错误:', event);
messagesDiv.innerHTML += '<p style="color: red;">连接错误或已关闭!</p>';
if (eventSource.readyState === EventSource.CLOSED) {
console.log('SSE 连接已关闭。');
}
};
closeButton.addEventListener('click', () => {
eventSource.close();
messagesDiv.innerHTML += '<p style="color: gray;">连接已手动关闭。</p>';
});
</script>
</body>
</html>
服务器端 SSE 响应格式:
服务器端需要将响应的 Content-Type
设置为 text/event-stream
。每个事件由一个或多个以 \n
结尾的行组成,事件之间用两个 \n
分隔。
vbnet
// 示例服务器端响应
data: 这是第一条消息\n\n
data: 这是第二条消息\n
data: 包含多行数据\n\n
event: stock_update\n
data: {"symbol": "AAPL", "price": 175.50}\n\n
event: news_feed\n
data: 最新新闻头条:前端技术发展迅速!\n\n
id: 123\n
data: 这是一条带ID的消息\n\n
retry: 5000\n // 告诉浏览器如果连接断开,5秒后重试
data: 这是一条设置重试间隔的消息\n\n
SSE 都能传输什么类型的数据?
SSE 传输的数据类型非常简单,它只能传输纯文本字符串。
这是因为 SSE 规范定义了消息体中的 data:
字段,其内容就是纯文本。当你通过 event.data
访问接收到的数据时,它总是一个字符串。
尽管只能传输字符串,但你可以通过序列化的方式传输各种复杂的数据结构:
-
JSON 字符串 (最常见):
这是传输结构化数据(如对象、数组)最常用的方式。服务器将 JavaScript 对象或数组序列化为 JSON 字符串,客户端接收后使用
JSON.parse()
解析。- 服务器发送:
data: {"name": "Alice", "age": 30}\n\n
- 前端接收:
JSON.parse(event.data)
得到{ name: "Alice", age: 30 }
。
- 服务器发送:
-
CSV 字符串:
用于传输表格数据或简单列表。
- 服务器发送:
data: value1,value2,value3\n\n
- 前端接收:
event.data.split(',')
得到['value1', 'value2', 'value3']
。
- 服务器发送:
-
XML 字符串:
如果你的数据源是 XML 格式,也可以直接传输 XML 字符串,然后在客户端使用 DOMParser 解析。
- 服务器发送:
data: <user><name>Bob</name></user>\n\n
- 前端接收:
new DOMParser().parseFromString(event.data, "text/xml")
。
- 服务器发送:
-
Base64 编码的二进制数据:
虽然 SSE 本身不能直接传输二进制数据,但你可以将二进制数据(如小图片、PDF 等)Base64 编码为字符串,然后作为
data:
字段的值发送。客户端接收后,再进行 Base64 解码。- 服务器发送:
data: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0EAw\n\n
- 前端接收:
const img = new Image(); img.src = event.data;
- 服务器发送:
总结来说,SSE 传输的原始数据类型永远是字符串。 任何非字符串类型的数据都需要在服务器端被序列化(转换成字符串),然后在客户端被反序列化(从字符串还原)。JSON 是最推荐和最常用的序列化格式,因为它既易于读写,又支持复杂的数据结构。
SSE 的其他特性
- 自定义事件类型 (
event:
字段):
服务器可以通过event:
字段指定事件类型。客户端可以通过eventSource.addEventListener('your_event_type', handler)
来监听特定类型的事件,而不是所有消息都走onmessage
。 - Last Event ID (
id:
字段):
服务器可以为每个事件指定一个id:
。当连接中断并重新连接时,浏览器会自动在 HTTP 请求头中带上Last-Event-ID
字段,值为上次收到的最后一个事件的 ID。服务器可以利用这个 ID 来判断从何处继续发送事件,实现断点续传。 - 重试间隔 (
retry:
字段):
服务器可以通过retry:
字段指定客户端在连接断开后,应该等待多少毫秒再尝试重新连接。
SSE 与 WebSocket 的选择
-
选择 SSE:
- 只需要从服务器向客户端单向推送数据。
- 对实时性要求不是极高,允许少量延迟。
- 希望实现更简单的协议和更少的服务器资源消耗。
- 利用 HTTP/2 的多路复用特性。
- 内置重连机制。
-
选择 WebSocket:
- 需要客户端和服务器之间进行双向实时通信。
- 需要传输二进制数据。
- 对实时性要求极高。
- 需要更复杂的协议和状态管理。
SSE 是一个强大而简单的工具,适用于许多实时更新的场景,特别是在你不需要客户端向服务器发送大量实时数据时。