游手好闲,无所事事,危机四伏...轮子虽然多,不是自己的用起来总感觉怪怪的;自己造的,即使七歪八拐,也挺‘快乐’的(^o^)。
大部分我们使用socket的主要功能就是消息实时同步及心跳机制,自己弄之前网上找了一下,发现有挺多好用的socket封装库了,例如vue的vue-socket.io等。
/** * 判断是否支持websocket */ static isSupport() { return /\{\s*\[native code\]\s*\}/.test(WebSocket.toString()) } 复制代码
这里默认根据当前的网站协议判断ws的协议
window.location.protocol.toLowerCase() === 'https:'
,然后根据协议及参数进行url的拼装:
/** * 获取websocket完整链接 * @param {string} url 基础websocket URI */ static getUrl(url, params = {}, isHttps = false) { let _path = [url] let _prefix = 'ws://' if (isHttps) { _prefix = 'wss://' } _path.unshift(_prefix) let _query = [] for (let key in params) { _query.push(`${key}=${params[key]}`) } if (url.indexOf('?') > -1) { _path.push('&') } else { _path.push('?') } _path.push(_query.join('&')) return _path.join('') } 复制代码
根据上面功能点,初略的得出socket的基本参数配置:
<!--默认策略--> const reconnectStrategy = (count, base) => { return base * count } static defaultConfig = { // 是否时https连接(默认判断当前URL协议) isHttps: window.location.protocol.toLowerCase() === 'https:', // 重连时间step(为0表示不重连) reconnectTime: 6 * 1000, // 重连时间策略 reconnectStrategy, // 心跳时间间隔 heartBeatTime: 60 * 1000, // 服务器数据适配 serveAdapt: loop, // onopen回调 onopen: loop, // onerror回调 onerror: loop, // onmessage回调 onmessage: loop, // onclose回调 onclose: loop, // onreconnect回调 onreconnect: loop, } 复制代码
使用类的写法,这里直接实例化类就可以返回当前ws的实例对象;
loopCount
表示此次重连的次数,重连成功后重置,eventPoll
为简单的事件池用于注册一些简易的事件回调。
constructor(url, params = {}, config = {}) { // 事件注册池 this.eventPoll = {} // websocket初始化参数 this.params = params // 配置项 this.config = { ...WS.defaultConfig, ...config } this.url = WS.getUrl(url, this.params, this.config.isHttps) this.loopCount = 0 this._init() __log( '%c--> WEB SOCKET <--', `text-shadow: 0 1px 0 #ccc,0 2px 0 #c9c9c9, 0 3px 0 #bbb,0 4px 0 #b9b9b9, 0 5px 0 #aaa,0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1),0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2),0 5px 10px rgba(0,0,0,.25), 0 10px 10px rgba(0,0,0,.2),0 20px 20px rgba(0,0,0,.15); font-size:5em;color: transparent; ` ) } 复制代码
实例化后,就会执行ws的_init
方法,进行ws的创建及回调的监听,ws链接成功后通知用户并开启保活_onHeartBeat
,在保活阶段有个小坑(要实时判断当前ws是否属于OPEN状态,否则手动触发_onError
事件。):
/** * 初始化(注册回调) */ async _init() { if (WS.isSupport()) { this.ws = new WebSocket(this.url) this.ws.onopen = this._onOpen.bind(this) this.ws.onmessage = this._onMessage.bind(this) this.ws.onclose = this._onClose.bind(this) this.ws.onerror = this._onError.bind(this) } else { log('Your Browser is not support WebSocket.') } } /** * 心跳函数,状态不为OPEN时触发重连机制(必须在ws open后调用该函数) */ async _onHeartBeat() { await this._sleep(this.config.heartBeatTime) if (this.ws.readyState === STATE.OPEN) { log('>>>>ping>>>>') this.ws.send('ping') this._onHeartBeat() } else { this._onError(new Event('ws_close')) } } /** * ws开启回调 */ _onOpen() { log('websocket已打开,开启心跳') this.loopCount = 0 this.config.onopen(this.ws) this._onHeartBeat() } /** * 消息监听回调 * @param {Object} msg 服务器推送消息 */ _onMessage(msg) { try { let _data = JSON.parse(msg.data) this.config.onmessage(_data) let _events = [] // code码事件 let _code = Number(_data.code) let _codeEvents = this.eventPoll[_code] if (_codeEvents) { _events.push(..._codeEvents) } for (let _cb of _events) { _cb(_data) } } catch (error) { log('数据解析错误', error) } } /** * ws关闭回调 */ _onClose(e) { log(`>>>>Socket已关闭>>>>`) this.config.onclose(e) } /** * ws出错回调 */ _onError(e) { this.config.onerror(e) if (!this.config.reconnectTime) { return } let _delayTime = this.config.reconnectStrategy(this.loopCount++, this.config.reconnectTime) log(`>>>>${_delayTime / 1000}s后尝试重新连接>>>>`) this.config.onreconnect(_delayTime, this.loopCount) this._sleep(_delayTime).then(() => { log(`>>>>第${this.loopCount}次重新连接>>>>`) this._init() }) } 复制代码
_onError
回调中,判断reconnectTime
是否大于0(0表示不会重连),然后根据当前轮次及重连基础时间得出当前过多久开始重连_delayTime
(重连就是简单的重新初始化ws-即重新调用_init
)
以下是eventPoll的简易实现(类型及回调):/** * 订阅消息回调 * @param {Any} type 事件码 * @param {Function} cb 事件回调 * @returns {Function} 取消订阅事件 */ subscribe(type, cb) { let _poll = this.eventPoll[type] || (this.eventPoll[type] = []) let _index = _poll.indexOf(cb) if (_index === -1) { _poll.push(cb) _index = _poll.length - 1 } return () => { let _i=_poll.findIndex(p=>p===cb) if(_i>-1){ _poll.splice(_i, 1) return true } return false } } 复制代码
reconnectTime
为0是为了关闭_onError
中不断重连的机制判断):/** * 关闭重连,销毁 */ closeReconnect() { this.config.reconnectTime = 0 this.ws.close() } 复制代码
npm使用:
npm i '@qiangkun/sdk' import { Easysocket } from '@qiangkun/sdk' const EVENT_MAP = { 201: 'sw_eva_pack_update', 202: 'sw_eva_video_update', 203: 'sw_eva_nego_update', 204: 'sw_eva_wait_update', '5563': 'token_expire', '3002': 'login_other' } let sw = new Easysocket( 'xxx', { app_id: 'app_agency', access_token: getToken() }, { onerror: (e) => { console.log('error',e) }, onclose: () => { console.log('close') } } ) for (let code in EVENT_MAP) { sw.subscribe(code, msg => { <!--使用vue事件机制--> bus.$emit(EVENT_MAP[code], msg) }) } 复制代码
自己倒腾,锅自己认,自己背--
不间断的拨弄原生js其实也算是对自己能力提升有所帮助,拨弄不了后面反正还有一堆的库使用,这样边学习边积累还是不错的~_~。