SSE技术的基本理解以及在项目中的使用

最近在写项目时,了解了 SSE 的技术,通过这篇文章,简单的记录一下自己在项目中学习 SSE 的收获。

一、什么是 SSE?

1. 基本概念

Server-Sent Events (SSE) 是一种基于 HTTP 的服务器到客户端的 ​​ 单向实时通信技术 ​​,允许服务器主动向客户端单向推送数据的技术。

2. 核心特点

  • 单向通信:仅支持服务器到客户端的单项数据推送、
  • 基于 HTTP:使用简单,无需额外协议,兼容现有 HTTP 基础设施。
  • 长连接:通过长轮询(long-polling)机制保持连接,减少频繁建立连接的开销。
  • 自动重连:连接中断时,客户端会自动尝试重新连接。
  • 轻量协议:数据格式简单(纯文本),适合高频底数据量的场景。

3. 工作原理

示例图
在这里插入图片描述

文字描述:

  1. 客户端发起连接
// url为服务器接口地址
const eventSource = new EventSource('url')

// 浏览器向服务器发送一个 HTTP GET 请求,等待服务器响应。
  1. 服务器建立长连接

服务器响应头需包含:

HTTP/1.1 200 OK
Content-Type: text/event-stream
Connection: keep-alive
Cache-Control: no-Cache

# 服务器保持连接开放,准备推送数据
  1. 服务器推送数据

数据格式规则:

  • 每条消息以 data: 开头, 一两个换行符 \n\n 结尾。
  • 可定义自定义事件(如: event: update
data: Hello!\n\n
event: price\n
data: {"symbol": "BTC", "price": 42000}\n\n
  1. 客户端接收并处理数据

监听默认的 message 事件或自定义事件(如 price)

eventSource.onmessage = (e) => {
  /** 处理默认事件 */
}
eventSource.addEventListener('price', (e) => {
  /** 处理自定义事件 */
})
  1. 连接终止与重连

客户端或服务器可主动关闭连接:

eventSource.close() // 客户端主动关闭

若连接意外中断(如网络问题),客户端自动重连。

4. SSE 与 HTTP 的关系

  • 基于 HTTP/1.1 及以上 ​​:SSE 使用 HTTP 协议,兼容性良好。
  • ​ 轻量级协议 ​​:无需复杂握手(如 WebSocket),适合简单实时场景。
  • 数据分块传输 ​​:服务器通过 HTTP 分块编码(Chunked Encoding)持续发送数据。

5. 与 WebSocket 的比较

特性SSEWebSocket轮询(Polling)
通信方向单向(服务器->客户端)双向单向(客户端主动)
协议HTTPWebSocket(WS)HTTP
连接开销低(长连接)中(双向握手)高(频繁连接)
适用场景实时通知、数据监控聊天、游戏、协作低频更新

二、客户端 API – EventSource

1. EventSource

在 MDN 中介绍到:EventSource 接口是 web 内容与服务器发送事件通信的接口。一个 EventSource 实例会对 HTTP 服务器开启一个持久化的连接,以 text/event-stream 格式发送事件,此连接会一直保持开启直到通过调用 EventSource.close() 关闭。

可以通过下方代码检测某种浏览器是否有这个对象

if (`EventSource` in window) {
  // ...
}

使用 SSE 时,浏览器先创建一个 EventSource 实例,向服务器发起连接。

const eventSource = new EventSource(url)

url 是服务器的接口地址,例如:https://example.com/message/sse,如果需要跨域,可以指定第二个参数,打开 withCredentials 属性,表示是否一起发送 Cookie

const eventSource = new EventSource(url, { withCredentials: true })

EventSource 实例的 readyState 属性,表明连接的当前状态。该属性只读,可以取以下值。

  • 0:相当于 常量 EventSource.CONNECTING, 表示 连接还未建立,或者断线正在重连。
  • 1:相当于常量 EventSource.OPEN,表示连接已经建立,可以接受数据。
  • 2:相当于常量 EventSource.CLOSED,表示连接已断,且不会重连。

2. 基本用法

  1. 连接一旦建立,就会触发 open 事件,可以在 onopen 属性定义回调函数。
// 建立连接
const eventSource = new EventSource(url, { withCredentials: true })

eventSource.onopen = (e) => {
  // ...
}
  1. 客户端收到服务器发来的数据,就会触发 message 事件,可以在 onmessage 属性的回调函数。
eventSource.onmessage = (e) => {
  // e.data 就是服务器端传回的数据(文本格式)。
  const data = e.data
  // handle message
}
  1. 如果发生通信错误(比如连接中断),就会触发 error 事件,可以在 onerror 属性定义回调函数。
eventSource.onerror = (e) => {
  // handle error
}
  1. close 方法用于关闭 SSE 连接
eventSource.close()
  1. 自定义事件

默认情况下,服务器发来的数据,总是触发浏览器 EventSource 实例的 message 事件。开发者还可以自定义 SSE 事件,这种情况下,发送回来的数据不会触发 message 事件。

eventCource.addEventListener('price', (e) => {
  const data = event.data
  // handle message
})

上面的代码中,浏览器对 SSE 的 price 事件进行监听。注意:事件要前后端进行约定

三、我在 Vue3 项目中的使用

我在做 SSE 模块时,通过创建了一个 messageStore 仓库进行 SSE 相关内容的初始化和消息的处理。除此之外,原生的 EventSource 是无法满足项目中的需求的,因为原生的 EventSource 无法进行自定义请求头等,所以采用了一个第三方库: EventSourcePolyfill。它实现了与原生 EventSource 相同的 API,但扩展了原生 EventSource 的功能(如跨域请求、自定义请求头等),并解决了原生 EventSource 在 ​​IE、旧版浏览器 ​​ 中的兼容性问题。

1. ​​EventSourcePolyfill 与 EventSource 的关系 ​

特性EventSource​EventSourcePolyfill
定位 ​​浏览器原生对象第三方 Polyfill 库
​ 兼容性 ​​仅支持现代浏览器(Chrome、Firefox、Safari 等)支持旧浏览器(如 IE 10+、旧版 Android/iOS)
​ 功能扩展 ​​仅基础功能支持自定义请求头、跨域凭据、重试策略等
​ 依赖关系 ​​无需额外引入需通过 npm 或 CDN 引入库
​API 一致性 ​​完全一致完全兼容原生 API,无学习成本

2. 如何使用 EventSourcePolyfill?​

  1. 安装库
npm install event-source-Polyfill
  1. 引入并创建实例(基本与原生的 EventSource 一致)
import { EventSourcePolyfill } from 'event-source-polyfill'

// 创建 SSE 连接(支持自定义请求头和跨域凭据)
const eventSource = new EventSourcePolyfill('/sse-endpoint', {
  headers: {
    Authorization: 'Bearer xxxx' // 自定义请求头
  },
  withCredentials: true, // 跨域时携带 Cookie
  heartbeatTimeout: 30000 // 超时时间(毫秒)
})

// 监听消息(与原生 EventSource 完全一致)
eventSource.onmessage = (event) => {
  console.log('收到数据:', event.data)
}

3. 在项目中使用

import { defineStore } from 'pinia'
import { useUserStore } from '@/store/userStore'
import { EventSourcePolyfill } from 'event-source-polyfill'

export const useMessageStore = defineStore('message', {
  state: () => ({
    // 存储原始消息列表
    messages: [],
    // EventSourcePolyfill实例
    eventSource: null
    // ....处理消息的变量
  }),
  action: {
    // 初始化连接
    initSSE() {
      // 1. 关闭旧连接(防止重复连接)

      if (this.eventSource) {
        this.eventSource.close()
      }

      // 2. 需要进行token校验的话可以获取token
      const userStore = useUserStore()
      const token = userStore.getToken()

      // 3. 创建实例
      this.eventSource = new EventSourcePolyfill(URL, {
        headers: {
          Authorization: `Bearer ${token}`,
          Accept: 'text/event-stream'
        },
        withCredentials: true // 携带 cookies
        heartbeatTimeout: 60000 // 心跳检测
      })
      // 记录重连次数
      let reconnectAttempts = 0
      const MAX_RETRIES = 5 // 最大重试次数
      // 连接成功回调
      this.eventSource.onopen = (e) => {
        console.log('SSE 连接成功')
        // reconnectAttempts = 0; // 重置重试计数器
      };

      // 消息处理
      this.eventSource.onmessage = (event) => {
        try {
          this.handleMessage(event)
          } catch (error) {
            console.error('消息处理失败:', error)
          }
      };

      // 错误处理(核心修改)
      this.eventSource.onerror = (error) => {
          console.error('SSE 连接异常:', error)
          // 仅在连接完全关闭时重连
          if (this.eventSource?.readyState === EventSource.CLOSED) {
              console.log('连接已关闭,尝试重连...')
              this.handleSSEError(reconnectAttempts, MAX_RETRIES)
              reconnectAttempts++
          }
      };
    },
    closeSSE () {
      if (this.eventSource) {
          this.eventSource.close();
          this.eventSource = null;
      }
      if (this.reconnectTimer) {
          clearTimeout(this.reconnectTimer);
      }
    },
    handleMessage () {
      //....
    }
  }
})

以上是我对 SSE 的理解,本人还在学习阶段,能力有限,有什么不对的地方还请指出来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值