vue3 实时通讯 SSE

javascript 复制代码
/**
 * 原生 EventSource 轻量封装
 * 自动重连 & 任意事件监听
 * 支持自定义请求头(通过 URL 参数传递 Authorization)
 */
export default class SSE {
  private url: string;
  private es: EventSource | null;
  private retry: number;
  private headers?: Record<string, string>;

  constructor(url: string, headers?: Record<string, string>) {
    this.url = url
    this.es = null
    this.retry = 3000 // 重连间隔 ms
    this.headers = headers
    this.connect()
  }

  /* 建立连接 */
  connect(): void {
    // 如果有 headers,将 Authorization 添加到 URL 中(因为 EventSource 不支持自定义 headers)
    let finalUrl = this.url;
    if (this.headers?.Authorization) {
      const token = this.headers.Authorization.replace('Bearer ', '');
      const separator = this.url.includes('?') ? '&' : '?';
      finalUrl = `${this.url}${separator}authorization=${encodeURIComponent(token)}`;
    }

    this.es = new EventSource(finalUrl)

    this.es.addEventListener('open', () => {
      console.log('[SSE] connected')
    })

    this.es.addEventListener('error', () => {
      console.log('[SSE] disconnected, retrying...')
      this.es?.close()
      setTimeout(() => this.connect(), this.retry)
    })
  }

  /**
   * 订阅任意后端事件
   * @param {string} event  事件名(与后端 event:xxx 对应)
   * @param {Function} cb   回调 (data: any) => {}
   */
  subscribe<T = any>(event: string, cb: (data: T) => void): void {
    this.es?.addEventListener(event, (e: MessageEvent) => {
      try {
        const data = JSON.parse(e.data)
        cb(data)
      } catch (err) {
        console.error('[SSE] parse error', err)
      }
    })
  }

  /* 手动关闭连接 */
  close(): void {
    this.es?.close()
    console.log('[SSE] closed by client')
  }
}
javascript 复制代码
import { defineStore } from 'pinia';

// 生成唯一 clientId
function generateClientId(): string {
  const stored = localStorage.getItem('sse_client_id');
  if (stored) return stored;
  const newId = `client_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  localStorage.setItem('sse_client_id', newId);
  return newId;
}



export const useNotificationStore = defineStore('notification', {
  state: () => ({
    sse: null,
    // ...
  }),
  
  actions: {
    async initSSE() {
      if (this.sse) return;

      const clientId = generateClientId();

      const token = localStorage.getItem('token');
      if (!token) {
        console.error('[NotificationStore] No token found, cannot init SSE');
        return;
      }

      // 建立 SSE 连接,传递 Authorization 请求头
      const sseUrl = `xxx/xxx/xx?clientId=${clientId}`;
      this.sse = new SSE(sseUrl, {
        Authorization: `${token}`
      });

      /* 任务状态事件 */
      this.sse.subscribe<{ code: number; data: any; message: string }>('sse_task_status', (payload) => {
        console.log('sse监听-----------------------', payload)
        if (payload.code !== 200) return;

        // payload.data为实时数据
        // 取出数据放入store

     
      });
    },
    // 断开 SSE 连接
    disconnectSSE() {
      if (this.sse) {
        this.sse.close();
        this.sse = null;
        console.log('[NotificationStore] SSE connection closed');
      }
    },
  },
});
javascript 复制代码
// 建立连接
notificationStore.initSSE()

// 断开连接
notificationStore.disconnectSSE()
相关推荐
路光.3 天前
uniappVue2升级Vue3内存溢出解决方式
vue·vue3·uniapp
沙振宇3 天前
【Web】使用 Vue3+PlayCanvas 开发 3D 游戏(五)3D 模型鼠标交互控制
3d·vue3·鼠标·playcanvas
融化的雪3 天前
vue2.6访问sse不能流式返回的问题
sse·前端调用sse请求
nibabaoo6 天前
前端开发攻略---Vue3项目中实现指定区域的打印预览与 PDF 导出功能
vue3·js·打印预览pdf
nibabaoo6 天前
前端开发攻略---在 Vue 3 项目中使用 vue-i18n 实现国际化多语言
前端·javascript·国际化·i18n·vue3
沙振宇7 天前
【Web】使用Vue3+PlayCanvas开发3D游戏(四)3D障碍物躲避游戏2-模型加载
游戏·3d·vue3·vite·playcanvas
SuperEugene7 天前
Vue3 中后台实战:VXE Table 从基础表格到复杂业务表格全攻略 | Vue生态精选篇
前端·vue.js·状态模式·vue3·vxetable
p5l2m9n4o6q8 天前
Vue3后台管理系统布局实战:从零搭建Element Plus左右布局(含Pinia状态管理)
vue3·pinia·element plus·viewui·后台管理系统
梵得儿SHI8 天前
Vue3 生态工具实战进阶:API 请求封装 + 样式解决方案全攻略(Axios/Sass/CSS Modules)
前端·css·vue3·sass·api请求·样式解决方案·组合式api管理
行者-全栈开发8 天前
43 篇系统实战:uni-app 从入门到架构师成长之路
前端·typescript·uni-app·vue3·最佳实践·企业级架构