webscoket-mitt
Version:
A plugin library that implements native WebSocket
2 lines (1 loc) • 5.67 kB
JavaScript
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("mitt")):"function"==typeof define&&define.amd?define(["mitt"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).MicroIframe=t(e.mitt)}(this,(function(e){"use strict";const t=e();class s{static MessageType={Beat:"beat",Message:"message",WS_CONNECT:"ws:connect",WS_ERROR:"ws:error",WS_CLOSE:"ws:close",WS_EXIT:"ws:exit",WS_RECONNECT:"ws:reconnect",WS_RECONNECT_FAILED:"ws:reconnect-failed",WS_RECONNECT_SUCCESS:"ws:reconnect-success",WS_HEARBEAT_FAILED:"ws:heartbeat-failed"};static CreateLifecycleMessage(e,t){return{id:"ws_msg_id_"+Date.now(),event:e,data:t,timestamp:Date.now()}}static CreateMessage(e,t){return JSON.stringify({id:"ws_msg_id_"+Date.now(),event:e,data:t,timestamp:Date.now()})}static ParseMessage(e){try{return JSON.parse(e)}catch(t){return console.error("消息解析失败:",t,e),null}}}class n{ws=null;url;heartbeatInterval;heartbeatTimer=null;heartbeatMissCount=0;heartbeatMaxMissCount;reconnectTimer=null;reconnectDelay;maxReconnectDelay;reconnectCount=0;maxReconnectCount;sendQueue=[];maxQueueSize=1e3;manuallyClosed=!1;logging=!1;DEFAULT_OPTIONS={url:"",heartbeatInterval:3e4,heartbeatMaxMissCount:4,reconnectDelay:1e3,maxReconnectDelay:1e4,maxReconnectCount:5,logging:!1};static MessageType=s.MessageType;constructor(e){const t=Object.assign({},this.DEFAULT_OPTIONS,e);this.url=t.url,this.heartbeatInterval=t.heartbeatInterval,this.heartbeatMaxMissCount=t.heartbeatMaxMissCount,this.reconnectDelay=t.reconnectDelay,this.maxReconnectDelay=t.maxReconnectDelay,this.maxReconnectCount=t.maxReconnectCount,this.logging=t.logging}setOptions(e){const t=Object.assign({},this.DEFAULT_OPTIONS,e);this.url=t.url,this.heartbeatInterval=t.heartbeatInterval,this.heartbeatMaxMissCount=t.heartbeatMaxMissCount,this.reconnectDelay=t.reconnectDelay,this.maxReconnectDelay=t.maxReconnectDelay,this.maxReconnectCount=t.maxReconnectCount,this.logging=t.logging}connect(){if(!this.url)throw new Error("未设置服务地址");if(!this.ws||this.ws.readyState!==WebSocket.OPEN){this.manuallyClosed=!1;try{this.ws=new WebSocket(this.url)}catch(e){return console.error("[websocket-mitt]: ws连接失败:",e),void this.reconnect()}this.ws.onopen=()=>{if(this.flushSendQueue(),this.startHeartbeat(),this.reconnectCount>0){this.logging&&console.log("[websocket-mitt]: ws重连成功!");const e=s.CreateLifecycleMessage(s.MessageType.WS_RECONNECT_SUCCESS,"");t.emit(s.MessageType.WS_RECONNECT_SUCCESS,e)}else{this.logging&&console.log("[websocket-mitt]: ws连接成功!");const e=s.CreateLifecycleMessage(s.MessageType.WS_CONNECT,"");t.emit(s.MessageType.WS_CONNECT,e)}this.reconnectDelay=1e3,this.reconnectCount=0},this.ws.onmessage=e=>{const n=s.ParseMessage(e.data);n&&(this.logging&&console.log("[websocket-mitt]: ws接收到消息:",n),this.heartbeatMissCount=0,t.emit(s.MessageType.Message,n),n.event!==s.MessageType.Message&&t.emit(n.event,n))},this.ws.onclose=e=>{this.logging&&console.log("[websocket-mitt]: ws关闭连接,",e);const n=s.CreateLifecycleMessage(s.MessageType.WS_CLOSE,e);t.emit(s.MessageType.WS_CLOSE,n),this.stopHeartbeat(),this.reconnect()},this.ws.onerror=e=>{console.error("[websocket-mitt]: ws发生错误,",e);const n=s.CreateLifecycleMessage(s.MessageType.WS_ERROR,e);t.emit(s.MessageType.WS_ERROR,n)}}}reconnect(){if(this.manuallyClosed)return;if(this.reconnectCount>=this.maxReconnectCount){console.error(`[websocket-mitt] 已达到最大重连次数(${this.maxReconnectCount}),停止重连`);const e=s.CreateLifecycleMessage(s.MessageType.WS_RECONNECT_FAILED,"");return t.emit(s.MessageType.WS_RECONNECT_FAILED,e),void this.exit()}if(this.reconnectTimer)return;const e=Math.min(this.reconnectDelay,this.maxReconnectDelay);this.reconnectTimer=setTimeout((()=>{this.logging&&console.log(`[websocket-mitt] 开始进行 ${this.reconnectCount+1} 次重连`);const e=s.CreateLifecycleMessage(s.MessageType.WS_RECONNECT,this.reconnectCount+1);t.emit(s.MessageType.WS_RECONNECT,e),this.ws=null,this.connect(),this.reconnectTimer=null,this.reconnectDelay=Math.min(2*this.reconnectDelay,this.maxReconnectDelay),this.reconnectCount++}),e)}exit(){this.manuallyClosed=!0,this.stopHeartbeat(),this.sendQueue=[],this.ws&&(this.ws.close(1e3,"client close"),this.ws=null),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.logging&&console.log("[websocket-mitt] ws服务关闭!");const e=s.CreateLifecycleMessage(s.MessageType.WS_EXIT,"");t.emit(s.MessageType.WS_EXIT,e)}startHeartbeat(){this.stopHeartbeat(),this.heartbeatTimer=setInterval((()=>{if(this.ws?.readyState!==WebSocket.OPEN)return;if(this.heartbeatMissCount>=this.heartbeatMaxMissCount){console.warn("心跳检测连续失败,准备重连..."),this.stopHeartbeat();const e=s.CreateLifecycleMessage(s.MessageType.WS_HEARBEAT_FAILED,"");return t.emit(s.MessageType.WS_HEARBEAT_FAILED,e),void this.ws?.close()}const e=s.CreateMessage(s.MessageType.Beat,"");this.wsSendMessage(e),this.heartbeatMissCount++}),this.heartbeatInterval)}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),this.heartbeatMissCount=0}$on(e,s){t.on(e,s)}$emit(e,t){const n=s.CreateMessage(e,t);this.wsSendMessage(n)}wsSendMessage(e){if(this.ws&&this.ws.readyState===WebSocket.OPEN)try{this.ws.send(e)}catch(t){console.error("发送消息失败:",t),this.sendQueue.push(e)}else this.sendQueue.length>=this.maxQueueSize&&this.sendQueue.shift(),this.sendQueue.push(e)}flushSendQueue(){for(;this.ws&&this.ws.readyState===WebSocket.OPEN&&this.sendQueue.length>0;){const e=this.sendQueue.shift();e&&this.wsSendMessage(e)}}}return n}));