在JavaScript中实现SSE(Server-Sent Events)
SSE(Server-Sent Events,服务器推送事件)是一种用于实现服务器主动向客户端推送数据的Web技术,它基于HTTP协议,允许服务器通过长连接持续向客户端发送事件流数据,非常适合实时通知、实时数据更新等场景。下面我们会从基本原理、客户端实现、服务端示例以及注意事项几个方面来介绍如何在JavaScript中使用SSE。
SSE的基本工作原理
SSE的通信流程主要分为三步:首先客户端通过JavaScript创建EventSource对象,向服务端发起一个HTTP请求;然后服务端响应这个请求,设置特定的响应头,保持连接打开,并且按照SSE规定的格式持续向客户端发送数据;最后客户端通过监听EventSource对象的事件,接收并处理服务端推送的数据。
服务端需要返回的响应头必须包含Content-Type: text/event-stream,这是SSE协议的标识,告诉客户端当前返回的是事件流数据。每条推送的数据需要遵循格式规范,通常包含data:字段,多个data:字段可以用换行分隔,每条完整消息以两个换行符结束。
JavaScript客户端实现
浏览器原生提供了EventSource API来支持SSE,我们不需要额外引入第三方库就可以直接使用。下面是一个基础的客户端实现示例:
// 创建EventSource对象,传入服务端的SSE接口地址
// 注意:EventSource只支持GET请求,不支持POST等其他请求方法
const eventSource = new EventSource('http://ipipp.com/sse/stream');
// 监听默认的消息事件,服务端发送的非指定事件类型的消息都会触发这个回调
eventSource.onmessage = function(event) {
console.log('接收到服务端推送的数据:', event.data);
// 这里可以编写处理推送数据的业务逻辑,比如更新页面内容
const dataContainer = document.getElementById('sse-data');
if (dataContainer) {
dataContainer.innerHTML += `<p>${event.data}</p>`;
}
};
// 监听连接打开事件
eventSource.onopen = function() {
console.log('SSE连接已成功建立');
};
// 监听错误事件
eventSource.onerror = function(error) {
console.error('SSE连接发生错误:', error);
// 连接出错时,EventSource会自动尝试重连,我们也可以手动处理重连逻辑
// 如果连接已经关闭,readyState会变成2,此时可以手动关闭对象避免无效重连
if (eventSource.readyState === EventSource.CLOSED) {
console.log('SSE连接已关闭');
}
};
// 监听自定义事件,如果服务端发送的消息带有event字段,会触发对应名称的事件
eventSource.addEventListener('update', function(event) {
console.log('接收到自定义update事件的数据:', event.data);
});上面的代码中,我们首先创建了EventSource实例,指定了服务端的SSE接口地址。然后分别监听了默认消息事件、连接打开事件、错误事件,还添加了一个自定义事件的监听。当服务端推送数据时,对应的回调函数就会执行,我们可以在回调中处理接收到的数据。
EventSource对象有三个状态,分别对应readyState属性的值:0表示连接还未建立,1表示连接已建立可以接收数据,2表示连接已关闭。我们可以通过这个属性判断当前连接的状态。
服务端示例(Node.js)
为了验证客户端的SSE功能,我们需要一个支持SSE的服务端。下面是一个简单的Node.js Express服务端的实现示例:
const express = require('express');
const app = express();
const port = 3000;
// 允许跨域,方便前端本地调试
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET');
next();
});
// SSE接口
app.get('/sse/stream', (req, res) => {
// 设置响应头,必须包含Content-Type: text/event-stream
res.setHeader('Content-Type', 'text/event-stream');
// 关闭缓存,避免数据被缓存导致推送不及时
res.setHeader('Cache-Control', 'no-cache');
// 保持连接打开
res.setHeader('Connection', 'keep-alive');
// 定时向客户端推送数据
let count = 0;
const pushInterval = setInterval(() => {
count++;
// SSE消息格式:data字段后面跟数据,每条消息以两个换行符结束
// 如果是自定义事件,可以在前面加上event: 事件名
const message = `data: 当前计数:${count}\n\n`;
res.write(message);
// 推送自定义事件示例
if (count % 5 === 0) {
const customEvent = `event: update\ndata: 计数达到${count},触发更新事件\n\n`;
res.write(customEvent);
}
// 推送10次后结束连接
if (count >= 10) {
clearInterval(pushInterval);
res.end();
}
}, 1000);
// 客户端断开连接时,清理定时器
req.on('close', () => {
clearInterval(pushInterval);
console.log('客户端断开SSE连接');
});
});
app.listen(port, () => {
console.log(`SSE服务运行在 http://ipipp.com:${port}`);
});这个Node.js服务启动后,会提供一个/sse/stream的GET接口,每1秒向客户端推送一次计数数据,每5次推送一个自定义update事件,推送10次后主动关闭连接。服务端返回的每条SSE消息都严格遵循格式规范,确保客户端可以正确解析。
注意事项
EventSource仅支持GET请求,如果需要传递参数,只能拼接在URL的查询字符串中,不支持请求体。- IE浏览器完全不支持
EventSourceAPI,如果需要兼容IE,可以使用第三方polyfill库,或者改用WebSocket实现类似功能。 - 如果服务端需要认证,可以在创建
EventSource时通过URL参数传递token,或者在支持的服务端配置中通过请求头传递,不过原生EventSource不支持自定义请求头,这是它的一个限制。 - SSE是基于HTTP长连接的,如果网络不稳定导致连接断开,
EventSource会自动尝试重新连接,默认的重连间隔是3秒,服务端也可以通过返回retry:字段自定义重连时间。