Эх сурвалжийг харах

perf(base-S-W): WebSocket 三断点修复——useWebSocket 不再覆写 open/close 且 connect 幂等(同url复用/异url先真close);balancePress 两页匿名监听改命名+卸载 offWebSocket+Modal.destroy;useVentWebSocket 真 close/防 watcher 叠加/开启自动重连

86132 1 долоо хоног өмнө
parent
commit
d012053473

+ 16 - 2
src/hooks/web/useVentWebSocket.ts

@@ -27,8 +27,12 @@ const resultWebSocket = computed(() => {
 const data = computed(() => (resultWebSocket.value ? resultWebSocket.value.data : ''));
 const status = computed(() => (resultWebSocket.value ? resultWebSocket.value.status : ''));
 const stop = <Ref<WatchStopHandle>>ref();
+// [perf-base-v1] S-W 6d: 保存 vueuse 返回的真实 close,供 closeWebSocket 真实断开底层 socket
+let realClose: (() => void) | null = null;
 
 const startWatchEffect = () => {
+  // [perf-base-v1] S-W 6d: 先 stop 旧 watcher 再注册,防重复调用时 watcher 叠加
+  stop.value?.();
   stop.value = watchEffect(() => {
     if (status.value === 'OPEN' && !data.value) {
       try {
@@ -68,12 +72,14 @@ function connectWebSocket() {
   if (!resultWebSocket.value) {
     console.log('测试链接。。。。');
     const result = $useWebSocket(state.server, {
-      // 自动重连
-      autoReconnect: false,
+      // [perf-base-v1] S-W 6d: 开启自动重连(保留心跳检测)
+      autoReconnect: true,
       // 心跳检测
       heartbeat: true,
       //protocols: [token],
     });
+    // [perf-base-v1] S-W 6d: 缓存真实 close(vueuse 显式 close 后 autoReconnect 不再触发)
+    realClose = () => result.close();
     ventStore.setResultWebSocket(result);
     startWatchEffect();
   }
@@ -93,5 +99,13 @@ export function getRecordList() {
 
 export function closeWebSocket() {
   state.recordList = [];
+  // [perf-base-v1] S-W 6d: 真实关闭底层 socket 并停掉 watcher(原实现只清 store 引用,socket 仍存活)
+  try {
+    realClose?.();
+  } catch (e) {
+    console.warn('[VentWebSocket] close 异常(忽略):', e);
+  }
+  realClose = null;
+  stop.value?.();
   ventStore.setResultWebSocket(null);
 }

+ 25 - 6
src/hooks/web/useWebSocket.ts

@@ -5,6 +5,8 @@ import { useWebSocket, WebSocketResult } from '@vueuse/core';
 import { getToken } from '/@/utils/auth';
 
 let result: WebSocketResult<any>;
+// [perf-base-v1] S-W 6b: 记录当前连接的 url,供幂等判断(不同 url 需先断开再按新地址重连)
+let currentUrl = '';
 const listeners = new Map();
 
 /**
@@ -15,16 +17,33 @@ export function connectWebSocket(url: string) {
   //update-begin-author:taoyan date:2022-4-24 for: v2.4.6 的 websocket 服务端,存在性能和安全问题。 #3278
   const token = (getToken() || '') as string;
   console.log(url);
-  result = useWebSocket(url);
+  // [perf-base-v1] S-W 6b: 幂等化——同 url 且已 OPEN/CONNECTING 直接复用现有连接,避免重复 connect 造成多连接并存、消息重复分发
+  if (result) {
+    const status = unref(result.status);
+    if ((status === 'OPEN' || status === 'CONNECTING') && currentUrl === url) {
+      console.log('[WebSocket] 已存在活动连接,复用(status=' + status + ')');
+      return;
+    }
+    // [perf-base-v1] S-W 6b: 不同 url 或旧连接已关闭:先真实 close 旧实例(6a 修复后 close 为 vueuse 原始方法)
+    try {
+      result.close();
+    } catch (e) {
+      console.warn('[WebSocket] 关闭旧连接异常(忽略):', e);
+    }
+  }
+  currentUrl = url;
+  // [perf-base-v1] S-W 6a: 不再覆写 vueuse 返回的 open/close 为日志函数(原写法导致 close() 无法真实断开连接),
+  // 日志移入 onConnected/onDisconnected 回调;onMessage/onError 改走 options 传入,重连后新实例依然生效
+  result = useWebSocket(url, {
+    onConnected: () => onOpen(),
+    onDisconnected: (_ws, e) => onClose(e),
+    onError: (_ws, e) => onError(e),
+    onMessage: (_ws, e) => onMessage(e),
+  });
   //update-end-author:taoyan date:2022-4-24 for: v2.4.6 的 websocket 服务端,存在性能和安全问题。 #3278
   if (result) {
-    result.open = onOpen;
-    result.close = onClose;
-
     const ws = unref(result.ws);
     if (ws != null) {
-      ws.onerror = onError;
-      ws.onmessage = onMessage;
       console.log('WebSocket连接通了', result, ws);
     } else {
       console.log('WebSocket连接连接失败');

+ 15 - 3
src/views/vent/monitorManager/balancePressMonitor/components/balancePressHomeBD.vue

@@ -126,7 +126,7 @@
   import PasswordModal from '../../comment/components/PasswordModal.vue';
   import UpdatePassword from '../../comment/components/UpdatePassword.vue';
   import { useModal } from '/@/components/Modal';
-  import { connectWebSocket, onWebSocket } from '/@/hooks/web/useWebSocket';
+  import { connectWebSocket, onWebSocket, offWebSocket } from '/@/hooks/web/useWebSocket';
   import { getToken } from '/@/utils/auth';
   import { useUserStore } from '/@/store/modules/user';
   import { usePressControl } from '../hooks/useControl';
@@ -321,7 +321,12 @@
     // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
     const url = `${glob.wsUrl?.replace('https://', 'wss://').replace('http://', 'ws://')}/websocket/${userStore.getUserInfo.id}?token=${token}`;
     connectWebSocket(url);
-    onWebSocket((data: any) => {
+    // [perf-base-v1] S-W 6c: 匿名监听改命名函数,卸载时可 offWebSocket 精确移除(匿名函数无法移除,重复挂载会叠加)
+    onWebSocket(handleWarnWsMessage);
+  }
+
+  // [perf-base-v1] S-W 6c: 报警消息命名处理函数(回调体自原匿名函数原样迁移,未改逻辑)
+  function handleWarnWsMessage(data: any) {
       if (data.cmd !== 'topic' || data.topic !== 'warn') return;
       if (!data.msgTxt) return;
 
@@ -451,7 +456,6 @@
         default:
           break;
       }
-    });
   }
 
   const settingFormDisabled = ref(true);
@@ -486,6 +490,14 @@
   });
 
   onUnmounted(() => {
+    // [perf-base-v1] S-W 6c: 卸载时移除 WS 监听并销毁残留报警弹窗,防泄漏
+    offWebSocket(handleWarnWsMessage);
+    warnModal1?.destroy();
+    warnModal1 = null;
+    warnModal2?.destroy();
+    warnModal2 = null;
+    warnModal3?.destroy();
+    warnModal3 = null;
     destroy();
     if (timer) {
       clearTimeout(timer);

+ 15 - 3
src/views/vent/monitorManager/balancePressMonitor/components/balancePressHomeSP.vue

@@ -105,7 +105,7 @@
   import { usePressControlSP } from '../hooks/useControl';
   import { Modal } from 'ant-design-vue';
   import dayjs from 'dayjs';
-  import { connectWebSocket, onWebSocket } from '/@/hooks/web/useWebSocket';
+  import { connectWebSocket, onWebSocket, offWebSocket } from '/@/hooks/web/useWebSocket';
   import { useUserStore } from '/@/store/modules/user';
   import { getToken } from '/@/utils/auth';
   // import dayjs from 'dayjs';
@@ -288,7 +288,12 @@
     // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
     const url = `${glob.wsUrl?.replace('https://', 'wss://').replace('http://', 'ws://')}/websocket/${userStore.getUserInfo.id}?token=${token}`;
     connectWebSocket(url);
-    onWebSocket((data: any) => {
+    // [perf-base-v1] S-W 6c: 匿名监听改命名函数,卸载时可 offWebSocket 精确移除(匿名函数无法移除,重复挂载会叠加)
+    onWebSocket(handleWarnWsMessage);
+  }
+
+  // [perf-base-v1] S-W 6c: 报警消息命名处理函数(回调体自原匿名函数原样迁移,未改逻辑)
+  function handleWarnWsMessage(data: any) {
       if (data.cmd !== 'topic' || data.topic !== 'warn') return;
       if (!data.msgTxt) return;
 
@@ -418,7 +423,6 @@
         default:
           break;
       }
-    });
   }
 
   onMounted(() => {
@@ -443,6 +447,14 @@
   });
 
   onUnmounted(() => {
+    // [perf-base-v1] S-W 6c: 卸载时移除 WS 监听并销毁残留报警弹窗,防泄漏
+    offWebSocket(handleWarnWsMessage);
+    warnModal1?.destroy();
+    warnModal1 = null;
+    warnModal2?.destroy();
+    warnModal2 = null;
+    warnModal3?.destroy();
+    warnModal3 = null;
     destroy();
     if (timer) {
       clearTimeout(timer);