通过 SSE 流式传输的方式可以让我们不再通过轮询的方式获取服务端返回的结果,进而提升前端页面的性能。对于需要轮询的业务场景来说,采用 SSE 确实是一个更好的技术方案。
SSE
SSE 全称为 Server-sent events , 是一种基于 HTTP 协议的通信技术,允许服务器主动向客户端(通常是Web浏览器)发送更新。 是 HTML5 标准的一部分,设计初衷是用来建立一个单向的服务器到客户端连接,使得服务器可以实时地向客户端发送数据。
参数设置
- 使用 SSE 技术构建需要服务器实时推送信息到客户端的连接,只需要将传统的 http 响应头的 contentType 设置为 text/event-stream 。
- 并且为了保证客户端展示的是最新数据,需要将 Cache-Control 设置为 no-cache 。
- 在此基础上,SSE 本质是一个 TCP 连接,因此为了保证 SSE 的持续开启,需要将 Connection 设置为 keep-alive 。
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
SSE Demo
服务端
npm init -y
npm install express
数据的开头必须是特定的标识
EventSource 发送数据时需要以特定的关键字开头,如 data、event、id、retry 和 :(注释行)
- data:指定事件的数据内容。可以跨多行。
- event:指定事件的类型,用于自定义事件。
- id:指定事件的唯一标识符。客户端会保存这个 ID,并在重新连接时发送 Last-Event-ID 头,服务器可以利用这个 ID 发送从上次连接后更新的数据。
- retry:指定客户端在连接中断后重试连接的时间间隔(毫秒)。
//server.js
const express = require('express');
const app = express();
const PORT = 3000;
app.use(express.static('public'));
app.get('/events', function (req, res) {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.header("Access-Control-Allow-Origin", "*"); // 或者指定特定的域名
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
let startTime = Date.now();
const sendEvent = () => {
// 检查是否已经发送了10秒
if (Date.now() - startTime >= 10000) {
res.write(`event: close\ndata: {"message":"close"}\n\n`); // 发送一个特殊事件通知客户端关闭
res.end(); // 关闭连接
return;
}
const data = { message: 'Hello World', timestamp: new Date() };
res.write(`data: ${JSON.stringify(data)}\n\n`);
// 每隔2秒发送一次消息
setTimeout(sendEvent, 2000);
};
sendEvent();
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
客户端
index.html
- message 事件:当服务器发送一个消息时触发。
- open 事件:当连接被打开时触发。
- error 事件:当发生错误时触发。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SSE Example</title>
</head>
<body>
<h1>Server-Sent Events Example</h1>
<div id="messages"></div>
<script>
const evtSource = new EventSource('http://127.0.0.1:3000/events');
const messages = document.getElementById('messages');
// 监听默认的消息事件
evtSource.onmessage = function (event) {
const newElement = document.createElement("p");
console.log(event.data)
const eventObject = JSON.parse(event.data);
newElement.textContent = "Message: " + eventObject.message + " at " + eventObject.timestamp;
messages.appendChild(newElement);
};
// 监听自定义事件
evtSource.addEventListener('close', function (event) {
evtSource.close();
console.log('收到自定义关闭事件消息:', event.data);
});
// 监听'open'事件确认连接已建立
evtSource.addEventListener('open', () => {
console.log('SSE connection opened');
});
// 监听'error'事件处理错误情况
evtSource.addEventListener('error', (error) => {
console.error('SSE error:', error);
});
</script>
</body>
</html>
package.json
{
"name": "sse",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"serve": "node server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.19.2"
}
}