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)

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

相关推荐
玩电脑的辣条哥2 小时前
Python如何播放本地音乐并在web页面播放
开发语言·前端·python
ew452182 小时前
ElementUI表格表头自定义添加checkbox,点击选中样式不生效
前端·javascript·elementui
suibian52352 小时前
AI时代:前端开发的职业发展路径拓宽
前端·人工智能
画月的亮2 小时前
element-ui 使用过程中遇到的一些问题及解决方法
javascript·vue.js·ui
Moon.92 小时前
el-table的hasChildren不生效?子级没数据还显示箭头号?树形数据无法展开和收缩
前端·vue.js·html
m0_526119402 小时前
点击el-dialog弹框跳到其他页面浏览器的滚动条消失了多了 el-popup-parent--hidden
javascript·vue.js·elementui
垚垚 Securify 前沿站2 小时前
深入了解 AppScan 工具的使用:筑牢 Web 应用安全防线
运维·前端·网络·安全·web安全·系统安全
工业甲酰苯胺5 小时前
Vue3 基础概念与环境搭建
前端·javascript·vue.js
lyj1689975 小时前
el-tree选中数据重组成树
javascript·vue.js·elementui
mosquito_lover16 小时前
怎么把pyqt界面做的像web一样漂亮
前端·python·pyqt