比较

1 uni.$emit是同步执行的,当有大量监听器时可能阻塞主线程。其他方案都是异步执行(setTimeout或Promise),性能更好。
2 uni.$emit的内存管理最方便(页面卸载时自动清理),其他方案都需要手动清理(需手动取消注册/订阅/监听),但提供了更精细的控制。
3 uni.$emit耦合度最高,其他方案都能更好地实现解耦。
4 uni.$emit使用最简单,其他方案需要额外实现但提供了更多优势。

综合建议
1 ​简单项目/快速开发​:优先使用 uni.$emit,虽然有些缺点但开发效率最高。
2 ​中型项目/长期维护​:推荐使用事件总线方案,提供了良好的平衡。
3 ​大型复杂项目​:使用观察者模式,可以获得最好的架构和扩展性。
4 ​需要精细控制事件​:避免使用 uni.$emit,选择回调注册或观察者模式。
5 ​性能敏感场景​:避免 uni.$emit的同步特性,选择异步方案。

1 方案 2 优点 3 缺点 4 适用场景
回调函数注册(setTimeout)|简单直接,无需额外依赖|需要手动管理回调函数|简单项目,事件类型较少
观察者模式(setTimeout)|解耦更好,可扩展性强|需要实现观察者类|中大型项目,需要良好架构
事件总线(Promise)|功能全面,类似Vue事件系统|需要实现事件总线类|需要类似Vue事件系统的项目

使用回调函数注册机制

// 在socket.js中
export default {
  // ...其他代码...
  eventListeners: {}, // 存储事件监听器

  // 注册事件监听器
  on(eventName, callback) {
    if (!this.eventListeners[eventName]) {
      this.eventListeners[eventName] = [];
    }
    this.eventListeners[eventName].push(callback);
  },

  // 取消事件监听
  off(eventName, callback) {
    if (this.eventListeners[eventName]) {
      const index = this.eventListeners[eventName].indexOf(callback);
      if (index > -1) {
        this.eventListeners[eventName].splice(index, 1);
      }
    }
  },

  // 修改_setUpListeners方法
  _setUpListeners() {
    if (!this.socketTask) return;

    this.socketTask.onMessage((message) => {
      let res;
      try {
        res = socketStrToJson(message.data);
      } catch (e) {
        console.error('消息解析失败:', e);
        return;
      }

      // 分发事件
      this.dispatchEvent(res.event, res);
    });
  },

  // 事件分发方法
  dispatchEvent(eventName, data) {
    if (this.eventListeners[eventName]) {
      // 使用setTimeout确保异步执行,避免阻塞
      setTimeout(() => {
        this.eventListeners[eventName].forEach(callback => {
          try {
            callback(data);
          } catch (e) {
            console.error(`事件${eventName}处理出错:`, e);
          }
        });
      }, 0);
    }
  }
};

调用:
// 在页面中
import socket from '@/socket.js';

export default {
  onLoad() {
    // 注册事件监听
    socket.on('eventName', this.handleEvent);
  },

  onUnload() {
    // 取消事件监听
    socket.off('eventName', this.handleEvent);
  },

  methods: {
    handleEvent(data) {
      // 处理事件
    }
  }
}

方法三 使用观察者模式-订阅 Observer

// observer.js
export default class Observer {
  constructor() {
    this.observers = {};
  }

  subscribe(event, callback) {
    if (!this.observers[event]) {
      this.observers[event] = [];
    }
    this.observers[event].push(callback);
  }

  unsubscribe(event, callback) {
    if (this.observers[event]) {
      this.observers[event] = this.observers[event].filter(
        observer => observer !== callback
      );
    }
  }

  notify(event, data) {
    if (this.observers[event]) {
      // 异步执行避免阻塞
      setTimeout(() => {
        this.observers[event].forEach(observer => {
          try {
            observer(data);
          } catch (e) {
            console.error(`Observer for ${event} error:`, e);
          }
        });
      }, 0);
    }
  }
}

// socket.js
import Observer from './observer.js';

export default {
  observer: new Observer(),

  // ...其他代码...

  _setUpListeners() {
    if (!this.socketTask) return;

    this.socketTask.onMessage((message) => {
      let res;
      try {
        res = socketStrToJson(message.data);
      } catch (e) {
        console.error('消息解析失败:', e);
        return;
      }

      // 通知观察者
      this.observer.notify(res.event, res);
    });
  }
};

// 在页面中
import socket from '@/socket.js';

export default {
  onLoad() {
    // 订阅事件
    socket.observer.subscribe('eventName', this.handleEvent);
  },

  onUnload() {
    // 取消订阅
    socket.observer.unsubscribe('eventName', this.handleEvent);
  },

  methods: {
    handleEvent(data) {
      // 处理事件
    }
  }
}

方法二 使用事件总线(Event Bus)event-bus.js

// 使用事件总线(Event Bus)event-bus.js
export default class EventBus {
  constructor() {
    this.events = {};
  }

  $on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }

  $off(event, callback) {
    if (this.events[event]) {
      if (callback) {
        this.events[event] = this.events[event].filter(cb => cb !== callback);
      } else {
        delete this.events[event];
      }
    }
  }

  $emit(event, ...args) {
    if (this.events[event]) {
      // 使用微任务确保异步执行
      Promise.resolve().then(() => {
        this.events[event].forEach(callback => {
          try {
            callback(...args);
          } catch (e) {
            console.error(`EventBus ${event} handler error:`, e);
          }
        });
      });
    }
  }
}

// socket.js
import EventBus from './event-bus.js';

export default {
  eventBus: new EventBus(),

  // ...其他代码...

  _setUpListeners() {
    if (!this.socketTask) return;

    this.socketTask.onMessage((message) => {
      let res;
      try {
        res = socketStrToJson(message.data);
      } catch (e) {
        console.error('消息解析失败:', e);
        return;
      }

      // 触发事件
      this.eventBus.$emit(res.event, res);
    });
  }
};

调用:
// 在页面中
import socket from '@/socket.js';
export default {
  onLoad() {
    // 监听事件
    socket.eventBus.$on('eventName', this.handleEvent);
  },

  onUnload() {
    // 取消监听
    socket.eventBus.$off('eventName', this.handleEvent);
  },

  methods: {
    handleEvent(data) {
      // 处理事件
    }
  }
}

方法一 uni.$emit

// 在页面中
import socket from '@/socket.js';

export default {
  created() {
    // 监听
    uni.$on(this.incidentName,this.onMessage)
  },

  beforeDestroy() {
    uni.$off(this.incidentName) // 销毁监听,释放内存
  },

  methods: {
    onMessage(data) {
      // 处理事件
    }
  }
}

socket.js

// socket.js
import EventBus from './event-bus.js';
import {
    socketFormat,
    socketStrToJson
} from '@/utils/util.js'
import config from '../config';
import Vue from 'vue'
import worker from '../minxins/worker';

// #ifdef WEB
import {
    MessageBox
} from 'element-ui';
import modal from '@/plugins/web-modal.js'
// #endif

// #ifndef WEB
import _modal from '@/plugins/modal.js'
// #endif

export default {
    eventBus: new EventBus(),
    socketTask: null, // WebSocket 连接实例
    isConnected: false, // 连接状态
    heartbeatInterval: null, // 心跳定时器
    heartbeatIntervalTime: 30000, // 心跳间隔时间(30 秒)
    heartbeatTimeout: 5000, // 心跳响应超时时间(5 秒)
    maxRetryCount: 3, // 最大重连次数
    retryCount: 0, // 当前重连次数
    isSend: false,
    messageTask: [],
    maxMessageQueueSize: 100, // 消息队列最大长度
    requestTime: 0,
    responseTime: 0,
    reconnectDelay: 3000, // 重连延迟时间(3秒)

    // 初始化 WebSocket 连接
    init(workerObj) {
        const _this = this;

        // 清理现有连接
        this.cleanup();

        if (!config.baseUrl) {
            console.error('服务器地址未配置');
            return;
        }

        this.socketTask = uni.connectSocket({
            url: config.baseUrl,
            success: () => {
                console.log('connectSocket success');
            },
            fail: (err) => {
                console.error('WebSocket 连接失败:', err);
                this.handleConnectionFailure(err);
            }
        });

        this.socketTask.onOpen(() => {
            console.log('WebSocket 连接成功', this.socketTask);
            this.handleConnectionSuccess();
        });

        this.socketTask.onClose(() => {
            console.log('WebSocket 已关闭');
            this.handleConnectionClose();
        });

        this.socketTask.onError((err) => {
            console.error('WebSocket 错误:', err);
            this.handleConnectionFailure(err);
        });
    },

    // 清理现有连接和状态
    cleanup() {
        if (this.socketTask) {
            this.socketTask.close();
            this.socketTask = null;
        }
        this.stopHeartbeat();
        this.isConnected = false;
        this.messageTask = [];
        this.isSend = false;
    },

    // 处理连接成功
    handleConnectionSuccess() {
        this.isConnected = true;
        this.retryCount = 0;
        this._setUpListeners();
        this.startHeartbeat();
        // 连接成功后处理积压的消息
        if (this.messageTask.length > 0) {
            this.processMessage();
        }
    },

    // 处理连接关闭
    handleConnectionClose() {
        this.isConnected = false;
        this.socketTask = null;
        this.stopHeartbeat();
        // 非主动关闭时尝试重连
        if (this.retryCount < this.maxRetryCount) {
            this.retryConnection();
        }
    },

    // 处理连接失败
    handleConnectionFailure(err) {
        this.isConnected = false;
        this.stopHeartbeat();
        this.retryConnection();
    },

    // 设置接收消息的监听器
    _setUpListeners() {
        if (!this.socketTask) return;

        this.socketTask.onMessage((message) => {
            console.log(`%c服务器响应数据:`, "background: blue; color: white;", message.data);

            let res;
            try {
                res = socketStrToJson(message.data);
            } catch (e) {
                console.error('消息解析失败:', e);
                return;
            }

            if (res && res.data && res.data.code !== 200) {
                this.showError(res.data.message);
            }

            console.log('事件名称:', res.event);
            console.log('响应结果', res);

            // 触发全局事件 方法一
            // uni.$emit(res.event + "", res);

            // 触发事件 方法二
            this.eventBus.$emit(res.event, res);

            // 更新最后响应时间
            this.responseTime = Date.now();

            if (res.data === 'pong') {
                console.log('收到心跳响应');
            }
        });
    },

    // 显示错误消息
    showError(message) {
        // #ifdef WEB
        modal.msgError(message);
        // #endif
        // #ifndef WEB
        _modal.showToast(message);
        // #endif
    },

    /**
     * 发送消息
     * @param {Object} msg 消息对象
     */
    sendMessage(msg) {
        if (!msg) return;

        // 检查消息队列长度
        if (this.messageTask.length >= this.maxMessageQueueSize) {
            console.warn('消息队列已满,丢弃最早的消息');
            this.messageTask.shift();
        }

        this.messageTask.push(msg);

        if (!this.isSend && this.isConnected) {
            this.processMessage();
        }
    },

    // 处理消息队列
    processMessage() {
        if (this.messageTask.length === 0) {
            this.isSend = false;
            return;
        }

        this.isSend = true;
        const msg = this.messageTask[0]; // 先不移除,等发送成功再移除

        if (!this.socketTask || this.socketTask.readyState !== 1) {
            console.warn('WebSocket未连接,暂停发送消息');
            this.isSend = false;
            return;
        }

        this.socketTask.send({
            data: msg,
            success: () => {
                console.log('消息发送成功:', msg);
                this.messageTask.shift(); // 发送成功后才移除
                setTimeout(() => this.processMessage(), 100);
            },
            fail: (err) => {
                console.error('消息发送失败:', err);
                // 发送失败时保持消息在队列中,稍后重试
                setTimeout(() => this.processMessage(), 1000);
            }
        });
    },

    /**
     * 检查 WebSocket 是否已连接
     * @param {Function} callback 回调函数
     */
    checkSocketReady(callback) {
        if (!callback) return;

        if (this.socketTask && this.socketTask.readyState === 1) {
            callback();
        } else {
            const listener = () => {
                if (this.socketTask && this.socketTask.readyState === 1) {
                    this.socketTask.offOpen(listener);
                    callback();
                }
            };
            this.socketTask?.onOpen(listener);
        }
    },

    // 关闭连接
    close() {
        this.retryCount = this.maxRetryCount; // 阻止自动重连
        if (this.socketTask) {
            this.socketTask.close({
                success: () => {
                    console.log('WebSocket 连接关闭');
                    this.cleanup();
                },
                fail: (err) => {
                    console.error('WebSocket 关闭失败:', err);
                    this.cleanup();
                }
            });
        }
    },

    // 启动心跳检测
    startHeartbeat() {
        this.stopHeartbeat(); // 先停止现有的心跳

        this.heartbeatInterval = setInterval(() => {
            if (!this.socketTask || this.socketTask.readyState !== 1) {
                console.warn('WebSocket 未连接,停止心跳检测');
                this.stopHeartbeat();
                return;
            }

            // 检查上次心跳响应是否超时
            const now = Date.now();
            if (now - this.responseTime > this.heartbeatTimeout) {
                console.warn('心跳响应超时,尝试重连');
                this.handleConnectionFailure(new Error('心跳响应超时'));
                return;
            }

            console.log('发送心跳: ping');
            this.requestTime = now;
            const msg = socketFormat('Ping', 'pong', {});
            this.sendMessage(msg);
        }, this.heartbeatIntervalTime);
    },

    // 停止心跳检测
    stopHeartbeat() {
        if (this.heartbeatInterval) {
            clearInterval(this.heartbeatInterval);
            this.heartbeatInterval = null;
            console.log('心跳检测已停止');
        }
    },

    // 重连逻辑
    retryConnection() {
        if (this.retryCount >= this.maxRetryCount) {
            console.error('已达到最大重连次数,停止重连');
            this.showReconnectDialog();
            return;
        }

        this.retryCount++;
        console.log(`尝试重连,第 ${this.retryCount} 次`);

        // 使用指数退避算法增加重连间隔
        const delay = Math.min(
            this.reconnectDelay * Math.pow(2, this.retryCount - 1),
            30000 // 最大30秒
        );

        setTimeout(() => {
            this.init();
        }, delay);
    },

    // 显示重连对话框
    showReconnectDialog() {
        const _this = this;
        // #ifdef WEB
        MessageBox.alert("WebSocket 连接失败,请点击确认进行重连", "系统提示", {
            type: 'error',
            showCancelButton: true,
            callback: function(action) {
                if (action === 'confirm') {
                    _this.retryCount = 0; // 重置重连计数
                    _this.init();
                }
            }
        });
        // #endif
        // #ifndef WEB
        _modal.confirm('连接中断,请点击确认进行重连', '系统提示').then(action => {
            if (action) {
                _this.retryCount = 0; // 重置重连计数
                _this.init();
            }
        });
        // #endif
    }
};
posted on 2025-11-11 20:02  羽丫头不乖  阅读(16)  评论(0)    收藏  举报