SSE 本身不支持原生事件名称路由,但可通过服务端发送 event: 字段定义事件类型、前端用 addEventListener 订阅实现模块级定向刷新;需规范命名、隔离监听、妥善管理连接生命周期及错误降级。

JavaScript 中的 SSE(Server-Sent Events)本身不支持原生的“事件名称路由”机制,但可以通过服务端发送带 event: 字段的自定义事件类型,前端用 addEventListener 订阅对应名称,实现多模块、多卡片的定向刷新——关键在于事件命名规范 + 模块级监听隔离。
服务端发送带 event 名称的 SSE 数据
SSE 协议规定每条消息可包含 event:、data:、id: 等字段。服务端按业务维度生成不同 event 名,例如:
event: card-user-profile\n data: {"id":101,"name":"张三"}\n\nevent: module-dashboard-stats\n data: {"pv":1245,"uv":892}\n\nevent: card-order-20240517\n data: {"status":"shipped","tracking":"SF123456"}\n\n
注意:每个事件块以两个换行符结束;event: 后不能有空格;data: 值建议 JSON 序列化,便于前端解析。
前端为不同模块/卡片创建独立 EventSource 并监听指定 event
不推荐全局共用一个 EventSource 实例并用 onmessage 处理所有事件——那样需手动判断 event 类型,耦合高、易出错。更合理的方式是:
立即学习“Java免费学习笔记(深入)”;
- 每个卡片或模块维护自己的
EventSource实例(或复用单例但用addEventListener隔离) - 使用
source.addEventListener('card-user-profile', handler)订阅专属事件 - handler 内直接更新对应 DOM 或状态,无需额外 if 判断
示例(模块级封装):
class CardUpdater {
constructor(eventType, selector) {
this.source = new EventSource('/api/sse');
this.el = document.querySelector(selector);
this.source.addEventListener(eventType, e => {
const data = JSON.parse(e.data);
this.render(data);
});
}
render(data) {
this.el.innerHTML = `<h3>${data.name}</h3><p>ID: ${data.id}</p>`;
}
}
// 使用
new CardUpdater('card-user-profile', '#user-card');
new CardUpdater('card-order-20240517', '#order-card-20240517');
避免重复连接与内存泄漏的关键细节
多个 EventSource 实例会发起多个 HTTP 连接(SSE 是长连接),需谨慎管理生命周期:
- 卡片销毁时调用
source.close(),防止无效监听和内存泄漏 - 服务端应支持按 event 类型做轻量级广播过滤(如只推送给订阅了该 event 的客户端),减少带宽浪费
- 若模块复用率高,可用单个 EventSource + 多个
addEventListener,但必须确保 event 名唯一且无歧义(如加前缀:module:dashboard、card:user-101)
补充:处理重连与错误降级
SSE 自动重连,但默认延迟约 3s。可通过服务端返回 retry: 字段控制:
-
retry: 5000\n表示 5 秒后重试 - 前端监听
source.onerror做 UI 提示或切换轮询兜底 - 对时效性要求极高的卡片,可结合
last-event-id实现断点续传(服务端需记录并返回)
不复杂但容易忽略。


















