vue3 中使用 sse 最佳实践,封装工具

工具

ts 复制代码
// 接受参数
export interface SSEChatParams {
    url: string,// sse 连接
    onmessage: (event: MessageEvent) => void,// 处理消息的函数
    onopen: () => void,// 建立连接触发的事件
    finallyHandler: () => void,// 相当于 try_finally 中的 finally 部分,不管出现异常或者关闭必然会执行的代码块
}


class SSEService {
    private eventSource: EventSource | null = null;

    private finallyHandler: (() => void) | undefined;

    // 建立连接
    connect(sseChatParams: SSEChatParams) {

        this.finallyHandler = sseChatParams.finallyHandler;

        this.eventSource = new EventSource(sseChatParams.url);

        if (sseChatParams.onopen != null) {
            this.eventSource.onopen = sseChatParams.onopen;
        }else{
            this.eventSource.onopen = () => {
                console.log('SSE 连接已开启');
            };
        }

        if (sseChatParams.onmessage != null) {
            this.eventSource.onmessage = sseChatParams.onmessage;
        } else {
            this.eventSource.onmessage = (event) => {
                console.log('收到消息:', event.data);
            };
        }

        this.eventSource.onerror = (error) => {
            if (this.eventSource?.readyState === EventSource.CLOSED) {
                console.log("SSE 连接已关闭");
            } else {
                console.error("SSE 错误:", error);
            }
            sseChatParams.finallyHandler();
        };
    }

    // 关闭连接
    disconnect() {
        if (this.eventSource) {
            this.eventSource.close();
            console.log("关闭 sse 连接")
            if (this.finallyHandler != null) {
                this.finallyHandler();
            }
        }
    }
}

export const sseService = new SSEService();

使用

我在我代码中是这样使用的,就这么简单

ts 复制代码
const onopen = () => {
  console.log("建立无敌 sse 连接成功")
}
// 建立连接
let sseChatParams: SSEChatParams = {
  onopen,
  url: import.meta.env.VITE_GLOB_API_URL + 'sse/createConnect?clientId=' + userStore.getSseClientId(),
  onmessage: (event: MessageEvent) => {
    // 收到消息
    console.log('收到消息xsssx:', event.data);
    let chunk = event.data;
    if (chunk === '[DONE]') {
      sseService.disconnect()
      state.imageList = []
      chatGuide(chatStore.activeChatId).then(resp => {
        chatGuideList.value = resp.data.guideList
        scrollViewBottom()
      })
      return
    }
    chunk = JSON.parse(chunk)
    if (chunk.type === 'error') {
      errorText = chunk.content
      console.log("errorText", errorText);
      updateChatData(errorText)
      return;
    }
    chunk = chunk.content;
    if (!chunk) {
      return;
    }
    lastText = lastText + chunk
    // 更新聊天数据源中的对话
    updateChatData(lastText)
  },
  finallyHandler: () => {
    console.log("finallyHandler操作")
    sessionStatus.value = 0
    inputDisabled.value = false
    dataSources.value[dataSources.value.length - 1].loading = false
    loading.value = false
    if (!isMobile.value) {
      // 聚焦输入框
      inputRef.value?.focus()
    }
  }
};
sseService.connect(sseChatParams)

另外你可能还需要增加一下关闭触发时机

javascript 复制代码
// 当组件从 DOM 中卸载前执行的操作
onUnmounted(() => {
    sseService.disconnect()
})

这里需要提一嘴,关于 sse 中的 onopen 触发时机

当你和服务器建立 sse 连接的时候,如果后端没有通过 sse 返回给你消息的话,那么前端浏览器大概率是不会触发 onopen 事件。

所以当与后端建立连接后要注意咯~


最后介绍一下自己的网站术士 AI:术士AI 2.0 (shushiai.com)

有兴趣用用玩玩,最好也支持一下,谢谢

相关推荐
跟橙姐学代码1 分钟前
Python里的“管家婆”:带你玩转os库的所有神操作
前端·python·ipython
jingling5552 分钟前
uniapp | 快速上手ThorUI组件
前端·笔记·前端框架·uni-app
UrbanJazzerati2 分钟前
可拖拽的进度条组件实战:实现思路与Demo
前端·面试
Cache技术分享5 分钟前
188. Java 异常 - Java 异常处理规范
前端·后端
不一样的少年_8 分钟前
Vue3 后台分页写腻了?我用 1 个 Hook 删掉 90% 重复代码(附源码)
前端·vue.js·设计模式
一枚前端小能手11 分钟前
🔥 滚动监听写到手抽筋?IntersectionObserver让你躺平实现懒加载
前端·javascript
我是日安12 分钟前
从零到一打造 Vue3 响应式系统 Day 5 - 核心概念:单向链表、双向链表
前端·vue.js
骑自行车的码农13 分钟前
React SSR 技术解读
前端·react.js
遂心_14 分钟前
React中的onChange事件:从原理到实践的全方位解析
前端·javascript·react.js
GHOME15 分钟前
原型链的原貌
前端·javascript·面试