Web API 提供的 WebSocket 类,封装一个 Socket 类
import modal from '@/plugins/modal'
const baseURL = import . meta. env. VITE_APP_BASE_WS ;
const EventTypes = [ 'open' , 'close' , 'message' , 'error' , 'reconnect' ] ;
const DEFAULT_CHECK_TIME = 55 * 1000 ;
const DEFAULT_CHECK_COUNT = 3 ;
const DEFAULT_CHECK_DATA = { Type: 1 , Parameters: [ 'alive' ] } ;
const CLOSE_ABNORMAL = 1006 ; class EventMap { deps = new Map ( ) ; depend ( eventType, callback ) { this . deps. set ( eventType, callback) ; } notify ( eventType, event ) { if ( this . deps. has ( eventType) ) { this . deps. get ( eventType) ( event) ; } }
} class Socket extends WebSocket { heartCheckData = DEFAULT_CHECK_DATA ; heartCheckTimeout = DEFAULT_CHECK_TIME ; heartCheckInterval = null ; heartCheckCount = DEFAULT_CHECK_COUNT constructor ( options, dep, reconnectCount = 0 ) { let _baseURL = baseURLconst { url, protocols, query = { } , greet = null , customBase = null } = options; const _queryParams = Object. keys ( query) . reduce ( ( str, key ) => { if ( typeof query[ key] !== 'object' && typeof query[ key] !== 'function' ) { return str += str. length > 0 ? ` & ${ key} = ${ query[ key] } ` : ` ${ key} = ${ query[ key] } ` ; } else { return str; } } , '' ) ; if ( customBase) { _baseURL = customBase} super ( ` ${ _baseURL} ${ url} ? ${ _queryParams} ` , protocols) ; this . _currentOptions = options; this . _dep = dep; this . _reconnectCount = reconnectCount; greet && Object. assign ( this , { heartCheckData: greet} ) this . initSocket ( ) ; } initSocket ( ) { this . onopen = function ( e ) { this . _dep. notify ( 'open' , e) ; this . heartCheckStart ( ) ; } this . onclose = function ( e ) { this . _dep. notify ( 'close' , e) ; if ( e. code === CLOSE_ABNORMAL ) { if ( this . _reconnectCount < this . heartCheckCount) { this . _reconnectCount++ ; const _socket = new Socket ( this . _currentOptions, this . _dep, this . _reconnectCount) ; this . _dep. notify ( 'reconnect' , _socket) ; } else { return modal. msgError ( 'WebSocket重连失败, 请联系技术客服!' ) ; } } } this . onerror = function ( e ) { this . _dep. notify ( 'error' , e) ; } this . onmessage = function ( e ) { if ( e. data instanceof Blob ) { const reader = new FileReader ( ) reader. readAsArrayBuffer ( e. data) reader. onload = ( ev ) => { if ( ev. target. readyState === FileReader. DONE ) { this . _dep. notify ( 'message' , ev. target?. result) ; } } } else { try { const _parseData = JSON . parse ( e. data) ; this . _dep. notify ( 'message' , _parseData) ; } catch ( error) { console. log ( error) } } } } subscribe ( eventType, callback ) { if ( typeof callback !== 'function' ) throw new Error ( 'The second param is must be a function' ) ; if ( ! EventTypes. includes ( eventType) ) throw new Error ( 'The first param is not supported' ) ; this . _dep. depend ( eventType, callback) ; } sendMessage ( data, options = { } ) { const { transformJSON = true } = options; let result = data; if ( transformJSON) { result = JSON . stringify ( data) ; } this . send ( result) ; } closeSocket ( code, reason ) { this . close ( code, reason) ; } heartCheckStart ( ) { this . heartCheckInterval = setInterval ( ( ) => { if ( this . readyState === this . OPEN ) { let transformJSON = typeof this . heartCheckData === 'object' this . sendMessage ( this . heartCheckData, { transformJSON } ) ; } else { this . clearHeartCheck ( ) ; } } , this . heartCheckTimeout) } clearHeartCheck ( ) { clearInterval ( this . heartCheckInterval) ; } resetHeartCheck ( ) { clearInterval ( this . heartCheckInterval) ; this . heartCheckStart ( ) ; }
}
const defaultOptions = { url: '' , protocols: '' , query: { } ,
} export const useSocket = ( options = defaultOptions ) => { if ( ! window. WebSocket) return modal. msgWarning ( '您的浏览器不支持WebSocket, 请更换浏览器!' ) ; const dep = new EventMap ( ) ; const reconnectCount = 0 ; return new Socket ( options, dep, reconnectCount) ;
}
使用这个封装好的 useSocket 函数,以在 Vue3中使用为例
import { useSocket } from './socket.js'
const socket = ref ( null )
const initWebSocket = ( ) => { const options = { url: '/<your url>' , query: { } , } socket. value = useSocket ( options) socket. value. subscribe ( 'open' , ( ) => { console. log ( 'WebSocket连接成功!' ) const greet = 'hello' socket. value. sendMessage ( greet) } ) socket. value. subscribe ( 'close' , reason => { console. log ( 'WebSocket连接关闭!' , reason) } ) socket. value. subscribe ( 'message' , result => { console. log ( 'WebSocket接收到消息:' , result) } ) socket. value. subscribe ( 'error' , err => { console. log ( 'WebSocket捕获错误:' , err) } ) socket. value. subscribe ( 'reconnect' , _socket => { console. log ( 'WebSocket断开重连:' , _socket) socket. value = _socket} )
}
initWebSocket ( )
debug 我们的心跳检测是否有效
if ( this . _reconnectCount > 0 ) return ;
const tempTimer = setInterval ( ( ) => { this . close ( ) ; if ( this . _reconnectCount < 3 ) { console. log ( '重连' ) ; this . _reconnectCount++ ; const _socket = new Socket ( this . _currentOptions, this . _dep, this . _reconnectCount) ; this . _dep. notify ( 'reconnect' , _socket) ; } else { return clearInterval ( tempTimer) ; }
} , 3 * 1000 )