SSE库选型+fetch-event-source示例

SSE

SSE(Server-Sent Events) 是一种基于 HTTP 的单向推送技术,允许服务端在一个长连接中持续向客户端发送事件。

与 WebSocket 的双向通信不同,SSE 更适合一些只需要从服务器获取数据的场景,比如实时新闻更新、股票行情、通知系统等。

针对项目实际需求(需携带 Authorization 头、支持精细错误处理),评估了三种主流方案。

方案对比

原生浏览器 EventSource API 使用简单,但在鉴权、错误处理及非浏览器环境支持上存在明显限制。

特性 原生 EventSource event-source-polyfill @microsoft/fetch-event-source(推荐)
实现方式 浏览器原生 API 基于 XMLHttpRequest 模拟 EventSource 行为 基于 fetch 实现
请求方法 GET,http长连接 通常模拟 GET 基于 fetch,请求控制更灵活
自定义请求头 ❌ 不支持 ✅ 支持 ✅ 支持
鉴权方式 通常依赖 cookie 或 query 可传 Authorization 可传 Authorization
握手阶段校验 能力较弱 一般 可在 onopen 中校验状态码、content-type
错误处理 较弱 一般 细粒度:可区分 HTTP 错误、响应类型异常、流关闭等
连接控制 较弱 一般 支持 AbortController 便于主动断开
重试治理 不可控,依赖浏览器默认行为 一般 可结合业务逻辑自定义重试策略
Node / SSR 适配 不友好(Node.js 无原生支持) 主要面向浏览器 更容易适配具备 fetch 能力的运行环境
页面可见性感知 集成 Page Visibility API 页面隐藏自动断连,可见时恢复(可配置关闭)
适用场景 简单 SSE 兼容场景、旧方案改造 现代前端项目、鉴权复杂场景
Github stars - 2.1k ⭐ 2.8k ⭐
github地址 - https://github.com/Yaffle/EventSource https://github.com/Azure/fetch-event-source

综上,更推荐使用 @microsoft/fetch-event-source

  1. 基于 fetch 实现,请求控制更灵活,可携带 Authorization 等自定义请求头。
  2. 可在 onopen、onerror、onclose 对连接状态和异常进行更清晰的调试与处理,识别连接中断等问题。
  3. 支持 AbortController,便于业务侧结合页面生命周期主动中断连接。
    相比原生 EventSource 和 event-source-polyfill,它更符合"鉴权能力、连接控制、错误可观测性"的要求

fetch-event-source实践代码

这是一个订阅消息通知的接口示例展示👆

从浏览器调试面板可以看到,SSE 服务端返回的并不是一次性响应,而是持续推送的事件流。

其中,CONNECT 表示连接建立成功,UNREAD_COUNT 表示一条具体业务消息,消息体为 {"message":0}

前端可以在 onmessage 中根据事件类型分别处理连接状态和业务数据,这也是 SSE 特别适合通知、状态流等场景的原因。

javascript 复制代码
import { fetchEventSource, EventSourceMessage } from '@microsoft/fetch-event-source'

class FatalError extends Error {}
class RetriableError extends Error {}

interface NoticeSubscribeOptions {
  token: string
  appId?: string
  onConnected?: () => void
  onUnreadCount?: (count: number) => void
  onFatalError?: (message: string) => void
}

export function subscribeNoticeSSE(
  url: string,
  options: NoticeSubscribeOptions,
) {
  const controller = new AbortController()

  const task = fetchEventSource(url, {
    method: 'GET',
    signal: controller.signal,
    openWhenHidden: true,
    headers: {
      Accept: 'text/event-stream',
      Authorization: options.token.startsWith('Bearer ')
        ? options.token
        : `Bearer ${options.token}`,
      ...(options.appId ? { appId: options.appId } : {}),
    },

    async onopen(response) {
      if (response.status === 401 || response.status === 403) {
        throw new FatalError('SSE 鉴权失败')
      }

      if (!response.ok) {
        throw new RetriableError(`SSE 连接失败,HTTP ${response.status}`)
      }

      const contentType = response.headers.get('content-type') || ''
      if (!contentType.includes('text/event-stream')) {
        throw new FatalError(`接口返回的不是 SSE:${contentType}`)
      }
    },

    onmessage(event: EventSourceMessage) {
      switch (event.event) {
        case 'CONNECT':
          options.onConnected?.()
          return

        case 'UNREAD_COUNT': {
          try {
            const payload = JSON.parse(event.data) as { message?: number | string }
            options.onUnreadCount?.(Number(payload.message ?? 0))
          } catch {
            throw new FatalError('UNREAD_COUNT 消息格式错误')
          }
          return
        }

        default:
          console.debug('[SSE] unknown event:', event.event, event.data)
      }
    },

    onclose() {
      // 如果业务上不期望服务端主动结束,可以在这里抛错并交给 onerror 重试
      throw new RetriableError('SSE 连接已关闭')
    },

    onerror(error) {
      if (error instanceof FatalError) {
        options.onFatalError?.(error.message)
        throw error
      }

      console.warn('[SSE retry]', error)
      return 3000
    },
  })

  task.catch((error) => {
    if (!controller.signal.aborted) {
      console.error('[SSE stopped]', error)
    }
  })

  return () => controller.abort()
}
相关推荐
橘子星15 分钟前
基于 ES6 语法的 NLP 任务模块化开发实践
前端·javascript
玉宇夕落17 分钟前
Props的传递学习
前端
月光刺眼18 分钟前
JS 底层执行机制探讨:执行上下文、变量提升与调用栈
前端·javascript
|_⊙34 分钟前
Linux 信号
运维·服务器·前端
ZC跨境爬虫1 小时前
跟着 MDN 学 JavaScript day_1:什么是 JavaScript?
开发语言·前端·javascript·ecmascript
广州华水科技1 小时前
单北斗GNSS水库变形监测系统的应用与发展分析
前端
吠品1 小时前
PyTorch 踩坑:libtorch_cpu.so 找不到 iJIT_NotifyEvent 符号
前端·vue.js·elementui
qq_2518364571 小时前
基于java Web 日化商超库存管理系统设计与实现
java·开发语言·前端
xiaofeichaichai1 小时前
Vue 响应式原理
前端·javascript·vue.js
提子拌饭1331 小时前
模态窗鸿蒙PC Electron框架实现技术详解 - 饮料含糖量应用案例分析
前端·javascript·华为·electron·前端框架·开源·鸿蒙