- 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_COUNTconstructor(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)