优雅封装微信小程序Websocket(Remax版)

·  阅读 910

这是我参与更文挑战的第2天,活动详情查看: 更文挑战

使用了Remax很长的时间,之前使用javascript版本

在六月份项目准备使用Typescript重构,故重新思考整理一个版本

小小知识点

微信小程序的wx.connectSocket只能同时对一个ws/wss的websocket发起最多两次请求,对于多个不同的websocket地址可以使用最多5个连接。

微信SocketTask是先使用connectSocket建立连接后可以返回一个SocketTask的对象。可以将这个连接存储在当前内部。但是不推荐,因为当页面刷新后websocket的连接很可能会丢失。而使用本地的localstroage进行缓存是拿不到的socket. 所以建议,每次使用新的websocket连接的时候实例化一次。再传入相应的ws/wss连接进行链接操作。

Websocket的调用是有时序的 ( 连接 - 判断是否已打开 - 接收服务器的参数 - 发送给服务器信息 ) wx.connectSocket -> wx.onSocketOpen -> wx.onReceivedMsg -> wx.sendSocketMessage

所以当遇到无法发送或者接收服务器和推送数据到服务器的时候,应该检查一下执行时序的问题。


filename: src/utils/websocket.ts

import {
    connectSocket,
    closeSocket,
    onSocketOpen,
    onSocketClose,
    sendSocketMessage,
    onSocketMessage,
    getNetworkType,
    onNetworkStatusChange,
} from 'remax/wechat';

class Websocket {
    // 是否连接
    isConnect: boolean = false;
    // 当前网络状态
    private netWork: boolean = false;
    // 是否人为退出
    private isClosed: boolean = false;
    // 心跳检测频率
    private timeout: number = 3 * 1000;
    private timeoutObj: NodeJS.Timeout | undefined;
    // 当前重连次数
    private connectNum: number = 0;
    // 心跳检测和断线重连开关,true为启用,false为关闭
    heartCheck: boolean = false
    isReconnect: boolean = false
    // 连接信息
    wsUrl = ''
    // 消息队列
    messageQueue = []

    constructor(heartCheck: boolean = false, isReconnect: boolean = false) {
        // 心跳检测和断线重连开关,true为启用,false为关闭
        this.heartCheck = heartCheck;
        this.isReconnect = isReconnect;
    }

    // 心跳开始
    start() {
        const self = this;
        this.timeoutObj = setInterval(() => {
            sendSocketMessage({
                // 发送心跳信息
                data: JSON.stringify({
                    beat: 'dj',
                }),
                success() {
                    // console.log("发送心跳成功");
                },
                fail(err) {
                    console.log(err);
                    console.log('连接失败');
                    self.reConnect()
                    self.reset();
                },
            }).then();
        }, this.timeout);
    }

    // 心跳重置
    reset() {
        if (this.timeoutObj) {
            clearTimeout(this.timeoutObj);
        }
        // 返回this即可支持链式调用
        return this;
    }

    // 初始化连接
    init(wsUrl: string) {
        if (this.isConnect) {
            this.onSocketOpened()
            this.onNetworkChange()
            this.onSocketClosed()
        } else {
            // 检查网络状态
            const self = this;
            getNetworkType({
                success(res) {
                    if (res.networkType !== 'none') {
                        // 开始建立连接
                        console.log('开始连接')
                        self.connect(wsUrl)
                    } else {
                        self.netWork = false;
                        console.log('网络已断开');
                    }
                },
            }).then();
        }
    }

    // 连接websocket
    connect(wsUrl: string) {
        // connectSocket不支持异步调用
        let self = this;
        connectSocket({
            url: wsUrl,
            success(res) {
                if (res.errMsg == 'connectSocket:ok') {
                    self.isConnect = true
                    self.wsUrl = wsUrl
                    self.onSocketOpened()
                    self.onNetworkChange()
                    self.onSocketClosed()
                }
            },
            fail(err) {
                console.log(err)
            },
        });
    }

    // 重连方法,会根据时间频率越来越慢,分别为3秒,10秒,45秒
    reConnect() {
        console.log(this.connectNum)
        if (!this.isConnect) {
            if (this.connectNum < 3) {
                setTimeout(() => {
                    this.init(this.wsUrl);
                }, 3 * 1000);
                this.connectNum += 1;
            } else if (this.connectNum < 10) {
                setTimeout(() => {
                    this.init(this.wsUrl);
                }, 10 * 1000);
                this.connectNum += 1;
            } else {
                setTimeout(() => {
                    this.init(this.wsUrl);
                }, 45 * 1000);
                this.connectNum += 1;
            }
        }
    }

    // 关闭websocket连接
    close() {
        if (this.isConnect) {
            closeSocket().then()
            // 重置心跳并改变相应状态
            this.reset()
            this.isClosed = true;
            this.isConnect = false;
        } else {
            console.warn('没有websocket连接')
        }
    }

    // 监听websocket连接关闭
    onSocketClosed() {
        onSocketClose((err) => {
            console.log(`当前websocket连接已关闭,错误信息为:${JSON.stringify(err)}`);
            // 停止心跳连接
            if (this.heartCheck) {
                this.reset();
            }
            // 关闭已登录开关
            this.isConnect = false;
            // 检测是否是用户自己退出小程序
            if (!this.isClosed) {
                console.log('进来了这里...')
                // 进行重连
                if (this.isReconnect) {
                    this.reConnect();
                }
            }
        });
    }

    // 检测网络变化
    onNetworkChange() {
        let self = this;
        onNetworkStatusChange((res) => {
            if (res.isConnected) {
                self.isConnect = false;
                self.reConnect()
            }
        });
    }

    // socket已开启
    onSocketOpened() {
        onSocketOpen(() => {
            console.log('websocket已打开');
            // 打开已连接开关
            this.isConnect = true;
            this.netWork = true;
            // 发送心跳
            if (this.heartCheck) {
                this.reset().start();
            }
        });
    }

    // 接收服务器返回的消息
    onReceivedMsg(callBack: any) {
        onSocketMessage((msg) => {
            if (typeof callBack === 'function') {
                callBack(msg);
            }
        });
    }

    // 发送ws消息
    sendWebSocketMsg(options: any) {
        sendSocketMessage({
            data: options.data,
            success(res) {
                if (options.success && typeof options.success === 'function') {
                    options.success(res);
                }
            },
            fail(err) {
                if (options.fail && typeof options.fail === 'function') {
                    options.fail(err);
                }
            },
        }).then();
    }
}

const websocket = new Websocket(
    // true代表启用心跳检测和断线重连
    true,
    true,
);
export default websocket;
复制代码

如何使用呢?

filename: src/page/index.tsx

import * as React from 'react';
import {View, Text, Image, Button} from 'remax/wechat';
import styles from './index.css';
import websocket from "@/utils/wxsocket";

export default () => {
    const connectWs = () => {
        const wsUrl = `ws://测试或者线上的ws地址信息如果是https加密则使用wss://`
        if (!websocket.isConnect) {
            websocket.init(wsUrl)
        }
    }
    const closeWs = () => {
        if (websocket.isConnect) {
            websocket.close()
        }
    }
    return (
        <View className={styles.app}>
            <View className={styles.header}>
                <Image
                    src="https://gw.alipayobjects.com/mdn/rms_b5fcc5/afts/img/A*OGyZSI087zkAAAAAAAAAAABkARQnAQ"
                    className={styles.logo}
                />
                <Button onClick={() => connectWs()}>开始连接ws服务器</Button>
                <Button onClick={() => closeWs()}>关闭ws连接服务器</Button>
            </View>
        </View>
    );
};

复制代码

连接上后 image.png 关闭链接后 image.png

分类:
前端
标签:
分类:
前端
标签: