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)

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

相关推荐
_揽16 分钟前
html如何在一张图片上的某一个区域做到点击事件
前端·html
踢足球的,程序猿19 分钟前
从 Vue 2.0 进阶到 Vue 3.0 的核心技术解析指南
前端·javascript·vue.js·前端框架·html
冷凌爱21 分钟前
Fetch与Axios:区别、联系、优缺点及使用差异
前端·node.js·js
袁煦丞42 分钟前
跨平台终端王者Tabby:cpolar内网穿透实验室第632个成功挑战
前端·程序员·远程工作
Sailing44 分钟前
Grafana-mcp-analyzer:基于 MCP 的轻量 AI 分析监控图表的运维神器!
前端·node.js·mcp
阿山同学.1 小时前
AWS 亚马逊 S3存储桶直传 前端demo 复制即可使用
前端·javascript·aws
Jolyne_1 小时前
grid 实现完美的水平铺满、间隔一致的自适应布局
前端·css
sunly_1 小时前
Flutter:导航固定背景图,滚动时导航颜色渐变
android·javascript·flutter
西洼工作室1 小时前
【解决导航栏字体图标渲染导致文本闪烁问题】采用腾讯视频的解决方案
前端·css·css3
摸鱼仙人~1 小时前
深入理解Java单例模式:确保类只有一个实例
java·javascript·单例模式