前言
放假前接到一个需求,关于前端从服务器获取实时数据,之前用的是定时器,由于原因,诸多原因,产品拉会讨论新的方案,最后改成用SSE技术实现同样效果。
在此之前我对SSE等知识了解得比较少,因此在实践过程中写成一篇博客,和大家一起交流学习。
在实时 Web 应用开发中,我们经常需要从服务器获取实时数据。传统的轮询方式效率低下,而 WebSocket 虽然功能强大但实现复杂。介绍一种轻量级的实时服务器推送技术 ------SSE(Server-Sent Events),以及如何在 Express 后端和 Vue2 前端中实现它。
一、什么是 SSE?
SSE(Server-Sent Events,服务器发送事件)是一种基于 HTTP 协议的技术,允许服务器主动向客户端持续推送数据。它的工作方式很简单:
- 客户端发起一次 HTTP 请求建立连接
- 服务器保持这个连接不关闭,有新数据时主动推送给客户端
- 数据以简单的文本格式传输,每次推送都遵循特定格式
- 当连接意外断开时,客户端会自动尝试重连
SSE 就像服务器给客户端开了一个专属推送通道,服务器可以随时向这个通道发送消息,而不需要客户端反复请求。
二、SSE 与 WebSocket 的区别
SSE 和 WebSocke都能实现服务器向客户端推送数据,但适用场景不同:
特性 | SSE | WebSocket |
---|---|---|
通信方式 | 单向(服务器→客户端) | 双向(全双工) |
协议 | 基于 HTTP | 独立的 WebSocket 协议 |
实现复杂度 | 简单(浏览器原生支持) | 较复杂 |
重连机制 | 自带自动重连 | 需要手动实现 |
数据格式 | 文本(简单格式) | 二进制或文本 |
适用场景 | 通知、实时数据展示 | 聊天、游戏等双向交互 |
简单来说,如果你只需要服务器向客户端(单向通信) 推送数据(如实时监控面板、新闻推送),SSE 是更轻量的选择;如果需要双向通信(如即时聊天),则应该选择 WebSocket。
三、基础使用:前端如何使用 SSE?
现代浏览器(除 IE 外)原生支持 SSE,通过EventSource
API 即可轻松使用:
javascript
// 建立连接
const source = new EventSource('/api/sse-endpoint');
// 监听消息事件
source.onmessage = (event) => {
console.log('收到数据:', event.data);
// 处理数据...
};
// 监听连接打开
source.onopen = () => {
console.log('连接已建立');
};
// 监听错误
source.onerror = (error) => {
console.error('发生错误:', error);
};
// 关闭连接(必要时)
// source.close();
SSE 还支持自定义事件类型,让我们可以更灵活地处理不同类型的消息:
javascript
// 监听自定义事件
source.addEventListener('temperatureUpdate', (event) => {
console.log('温度更新:', event.data);
});
source.addEventListener('systemMessage', (event) => {
console.log('系统消息:', event.data);
});
四、实战演练
1、Express 后端实现 SSE
ps:如何创建express应用程序在本篇文章不会提及,有需要请阅读文章:
讲解如何在 Express 中实现 SSE 服务,主要分为以下几个步骤:
1. 设置正确的响应头
SSE 需要特定的 HTTP 头来告知客户端这是一个事件流:
javascript
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
2. 保持连接并发送数据
与普通 HTTP 请求不同,SSE 连接需要保持打开状态。我们使用res.write()
而不是res.send()
或res.end()
来发送数据:
javascript
// 存储所有客户端连接
let clients = [];
router.get('/', (req, res) => {
// 设置SSE响应头
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
// 将新客户端添加到列表
clients.push(res);
// 发送连接确认
res.write(`event: connected\n`);
res.write(`data: ${JSON.stringify({ message: '已建立SSE连接' })}\n\n`);
// 处理客户端断开
req.on('close', () => {
clients = clients.filter(client => client !== res);
});
});
3. 遵循 SSE 数据格式
服务器发送的每条消息必须遵循特定格式:
- 以
data:
开头,后跟实际内容 - 每条消息以两个换行符 (
\n\n
) 结束 - 可选指定
event:
字段来定义事件类型 - 可选指定
id:
字段来标识消息 - 可选指定
retry:
字段来建议重连间隔
javascript
// 定期向所有客户端发送数据
setInterval(() => {
const data = {
timestamp: new Date().toISOString(),
temperature: (20 + Math.random() * 10).toFixed(2),
humidity: (40 + Math.random() * 30).toFixed(2)
};
// 向每个连接的客户端发送数据
clients.forEach(client => {
client.write(`data: ${JSON.stringify(data)}\n\n`);
});
}, 2000);
4. 处理跨域问题
由于我们的前端和后端可能运行在不同端口,需要配置 CORS:
//安装
npm install cors
javascript
const cors = require('cors');
// 配置CORS允许所有来源
app.use(cors({
origin: "*",
methods: ["GET", "POST"],
allowedHeaders: ["Content-Type"]
}));
2、Vue2 前端实现 SSE 客户端
在 Vue2 中使用 SSE,我们可以创建一个专门的组件来管理连接和处理数据:
1. 连接管理
在组件中,我们可以通过按钮控制 SSE 连接的建立和关闭:
javascript
methods: {
toggleConnection() {
if (this.isConnected) {
this.disconnect();
} else {
this.connect();
}
},
connect() {
// 连接到后端SSE服务
this.eventSource = new EventSource('http://localhost:3000/sse');
// 监听消息事件
this.eventSource.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
this.addMessage(data, false);
this.startTypingEffect(this.messages.length - 1);
} catch (error) {
console.error('解析SSE数据失败:', error);
}
};
// 监听连接打开和错误事件...
},
disconnect() {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
this.isConnected = false;
}
}
}
2. 实现打印机效果
为了让新消息以打字机效果展示,我们可以逐字显示文本并添加光标闪烁动画:
javascript
startTypingEffect(messageIndex) {
const message = this.messages[messageIndex];
if (message.isSystem) return;
// 为每个字段启动打字效果
Object.keys(message.content).forEach((field, fieldIndex) => {
const text = message.content[field];
const elementSelector = `.message-item:nth-child(${messageIndex + 1}) .data-field:nth-child(${fieldIndex + 1}) .typed-text`;
this.$nextTick(() => {
const element = this.$el.querySelector(elementSelector);
if (element) {
let i = 0;
element.textContent = '';
const typingInterval = setInterval(() => {
if (i < text.length) {
element.textContent += text.charAt(i);
i++;
} else {
clearInterval(typingInterval);
}
}, 50); // 控制打字速度
}
});
});
}
配合 CSS 实现光标闪烁效果:
html
.typed-text {
border-right: 2px solid #333;
padding-right: 3px;
animation: blink 0.7s step-end infinite;
}
@keyframes blink {
from, to { border-color: transparent }
50% { border-color: #333 }
}
3、案例效果

在控制台查看长连接实时传递数据

总结
SSE 是一种简单高效的服务器推送技术,非常适合只需要服务器向客户端单向发送数据的场景。与 WebSocket 相比,它实现简单且基于 HTTP 协议,无需额外的服务器配置。
本文只是一个简单的案例,有不足之处还请大家点出。
