2

先看一下官方文档对 WebSocketSocketTask 的定义。

初始化的时候,都是使用uni.connectSocket()方法实现,但如果要使用WebsocketTask,需要注意这段话:

如果希望返回一个 socketTask 对象,需要至少传入 success / fail / complete 参数中的一个。

var socketTask = uni.connectSocket({
   url: 'wss://www.example.com/socket', //仅为示例,并非真实接口地址。
   complete: ()=> {}
});

如果没有传入 success / fail / complete 参数,则会返回封装后的 Promise 对象:Promise 封装

为了避免在切换页面时导致ws连接断开,我们可以将ws实例化时挂载至vue全局。

封装方法

// utils/websocket.js
export default class WebsocketTask {
    /** 心跳定时器 */
    #heartbeatInterval = null
    /** 回调函数 */
    #callback = null
    /** ws状态 */
    #websocketStatus = false
    /** 是否主动关闭 */
    #isClosed = false

    constructor (url, interval) {
        this.url = url
        this.intervalTime = interval
        
        // 实例化时立即连接ws。根据需求启用
        // try {
        //     return this.initWebsocket()
        // } catch (e) {
        //     this.#websocketStatus = false
        //     this.reconnect()
        // }
    }
    
    /**
     * 初始化Websocket
     */
    initWebsocket() {
        this.socketTask = uni.connectSocket({
            url: this.url,
            // 必要,否则会返回一个Promise
            success: (status) => { }
        })
    
        this.socketTask.onOpen(() => {
            clearTimeout(this.#heartbeatInterval)
            this.#isClosed = false
            this.#startHeartbeat()
            this.socketTask.onMessage((msg) => {
                let msgObj = JSON.parse(msg.data) || null

                if (msgObj.type === 'pong') {
                    if (this.#websocketStatus === false) console.log('%c [Websocket] ', 'background: #2888D9;', '初始化成功')
                    this.#websocketStatus = true
                } else {
                    console.log('%c [Websocket] ', 'background: #2888D9;', '接收消息:', msgObj)
                    this.#callback && this.#callback(msgObj)
                }
            })
        })
        
        // Websocket Closed Unexpected
        this.socketTask.onClose(() => {
            this.#websocketStatus = false
            console.log('%c [Websocket] ', 'background: #2888D9;', '断开连接!')
            // 非主动断开时自动重连
            if (!this.#isClosed) this.reconnect()
        })
    }
    
    /** 开始心跳 */
    #startHeartbeat() {
        this.#heartbeatInterval = setInterval(() => {
            this.sendMessage({type: 'ping'})
        }, this.intervalTime)
    }
    
    /**
     * 发送消息
     * @param {*} data 消息内容
     */
    sendMessage (data) {
        if (data.type !== 'ping') {
            // 未就绪时稍后重试
            if (!this.#websocketStatus) {
                console.warn(' [Websocket] 未就绪', JSON.stringify(data))
                return setTimeout(() => {
                    this.sendMessage(data)
                }, this.intervalTime);
            }
            console.log('%c [Websocket] ', 'background: #2888D9;', '发送消息:', JSON.stringify(data))
        }
        this.socketTask.send({
            data: JSON.stringify(data)
        })
    }

    /** 设置回调 */
    setCallback(cb) {
        this.#callback = cb
    }
    
    reconnect() {
        clearInterval(this.#heartbeatInterval)
        if(!this.#websocketStatus) {
            setTimeout(() => {
                this.initWebsocket()
            }, 3000);
        }
    }
    
    /** 主动断开连接 */
    disconnect() {
        uni.closeSocket()
        this.#websocketStatus = false
        this.#isClosed = true
        clearInterval(this.#heartbeatInterval)
    }
}

全局挂载

// main.js

// ...

App.mpType = 'app'
Vue.prototype.$websocket = new WebsocketTask('wss://demo.com', 3000)
const app = new Vue({
    ...App
})
app.$mount()

// ...

使用方法

// demo.vue

// ...
<script>
    // ...
    onLoad() {
        // 初始化ws
        this.$websocket.initWebsocket()
        
        // 设置回调
        this.$websocket.setCallback((msg) => {
            // 收到消息时的回调逻辑
            console.log(msg)
        })
    },
    onUnload() {
        // 主动断开连接
        this.$websocket.disconnect()
    },
    methods: {
        sendMsg(msg) {
            this.$websocket.sendMessage({value: 'hello, world'})
        },
    // ...
</script>

Andarinz
10 声望4 粉丝