From 8468d9bf4d127e273ec6c8e0d3849d8f60656e0e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 21 May 2026 08:41:44 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(im):=20=E4=BF=AE=20WebSocket?= =?UTF-8?q?=20=E9=87=8D=E5=A4=8D=E8=BF=9E=E6=8E=A5=EF=BC=9Aconnect=20?= =?UTF-8?q?=E5=85=A5=E5=8F=A3=E6=A3=80=E6=B5=8B=E6=97=A7=20socket=20?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=A4=8D=E7=94=A8=20/=20=E9=87=8D=E5=BB=BA?= =?UTF-8?q?=EF=BC=8Cdisconnect=20=E8=A7=A3=E7=BB=91=E5=85=A8=E9=83=A8=20ha?= =?UTF-8?q?ndler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/im/home/store/websocketStore.ts | 24 ++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/views/im/home/store/websocketStore.ts b/src/views/im/home/store/websocketStore.ts index 0effe9d4a..c03c3ca42 100644 --- a/src/views/im/home/store/websocketStore.ts +++ b/src/views/im/home/store/websocketStore.ts @@ -138,6 +138,9 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', { /** * 连接 WebSocket * 复用 yudao 内置 /infra/ws 通道,后端通过 sendObject(type, content) 下发 + * + * 调用契约:切账号 / token 刷新前必须先 `disconnect()` 再 `connect()`; + * 本方法不感知 token 变化,旧 socket 在 CONNECTING / OPEN 状态会直接复用旧 token,可能拿到错误身份 */ connect() { // 鉴权用 refreshToken(生命周期更长;access token 过期后服务端会通过 frame 通知重登) @@ -146,6 +149,23 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', { console.warn('[IM WS] refreshToken 为空,跳过连接') return } + // 旧 socket 还在 CONNECTING / OPEN 直接复用,避免叠加多份 onmessage 监听导致重复消息 / 提示音 / 已读上报 + const existingSocket = this.socket + if ( + existingSocket && + (existingSocket.readyState === WebSocket.OPEN || + existingSocket.readyState === WebSocket.CONNECTING) + ) { + return + } + // 旧 socket 已 CLOSING / CLOSED:解绑回调 + 清引用再 new,避免老 handler 仍持有 store 引用阻碍 GC + if (existingSocket) { + existingSocket.onopen = null + existingSocket.onmessage = null + existingSocket.onerror = null + existingSocket.onclose = null + this.socket = null + } const url = `${this.buildWsUrl()}/infra/ws?token=${refreshToken}` this.socket = new WebSocket(url) @@ -752,7 +772,9 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', { disconnect() { if (this.socket) { // close() 异步触发 onclose / onerror,回调里会无条件 reconnect; - // 主动关闭路径必须先解绑这两个 handler,否则 3 秒后会自动连回去 + // 主动关闭路径必须先全部解绑,否则 onclose 会引发自动重连,CONNECTING 期间的 in-flight message 也可能被老 onmessage 投递到 stale 上下文 + this.socket.onopen = null + this.socket.onmessage = null this.socket.onclose = null this.socket.onerror = null this.socket.close()