Vue2 使用WebSocket实现前端与后端的实时通讯

功能描述:WebSocket是一种在单个TCP连接上进行全双工通信的协议,它能够在客户端和服务器之间建立持久的连接,使得实时通讯成为可能。在前端开发中,WebSocket通常用于实现实时消息推送、实时数据更新等功能,为用户提供更流畅、即时的体验

本文将介绍如何在前端项目中利用WebSocket实现与后端的实时通讯,并结合具体的代码示例进行讲解。

实现单例模式的SocketService类

在本文中,我们将使用一个名为SocketService的类来封装WebSocket的相关操作,并通过单例模式确保在整个应用程序中只有一个SocketService实例存在。这样可以避免多次实例化和重复连接服务器的问题,同时方便在应用程序的不同模块中共享同一个SocketService实例,实现统一的数据传输和处理逻辑。

创建utils文件夹--->websocket.js文件

import { LoginWebSocketApi } from "@/request/api";
export default class SocketService {
    // 用于实现单例模式的写法
    // 可以确保在整个应用程序中只有一个SocketService实例存在,避免了多次实例化和重复连接服务器的问题。通过单例模式,可以方便地在应用程序的不同模块中共享同一个SocketService实例,实现统一的数据传输和处理逻辑。
    static instance = null;
    static get Instance() {
        if (!this.instance) {
            this.instance = new SocketService()
        }
        return this.instance
    }
    ws = null
    // 标识是否连接成功
    connected = false
    // 重试的次数
    sendRetryCount = 0
    // 重连的次数
    connectRetryCount = 0
    // 存储回调函数
    // callBackMapping = {}
    heartbeatInterval = null;
    // 连接服务器的方法
    connect(vue) {
        // 连接服务器
        if (!window.WebSocket) {
            return console.log("浏览器不支持webSocket")
        }
        if (!localStorage.getItem("token")) {
            console.log('没有token,不连接WebSocket');
            return;
        }
        this.ws = new WebSocket("wss://你的地址");
        // 连接成功的事件
        this.ws.onopen = () => {
            console.log("连接成功");
            this.connected = true;
            this.connectRetryCount = 0;
            clearTimeout(this.heartbeatInterval);
            this.start();
        }
        // 连接失败的事件
        this.ws.onclose = () => {
            console.log("连接失败")
            this.connected = false;

            if (this.connectRetryCount <= 100) {
                this.connectRetryCount++
            }
            setTimeout(() => {
                this.connect(vue)
            }, this.connectRetryCount * 500)
        }

        // 收到数据的事件
        this.ws.onmessage = (res) => {
            // console.log("获取信息", res.data)
            try {
                console.log('接收消息', res.data);
                let data_arr = JSON.parse(res.data);

                let type = data_arr.type;
                // 处理不同类型的消息
                switch (type) {
                    case 'login':
                        this.login_websocket(data_arr.client_id);
                        break;
                    case 'charge_normal_end':
                        vue.$bus.$emit('charge_normal_end', data_arr);
                        break;
                    case 'shots_count_change':
                        vue.$bus.$emit('shots_count_change', data_arr);
                        break;
                    default:
                        break;
                }
            } catch (e) {
                console.log('e', e);
            }
        }
    }

    login_websocket(client_id) {
        LoginWebSocketApi({
            client_id: client_id,
            ws_group: '首页'
        }).then((webRes) => {
            let ddd = {
                type: 'login_success',
                login_id: webRes.data.login_id
            };
            this.send(JSON.stringify(ddd));
            console.log('发送登录消息', ddd);
            this.manual_colse = false;
        }).catch((err) => {
            console.log('33err', err)
        });
    }
    // 发送
    send(data) {
        // this.ws.send(data)
        if (this.connected) {
            this.sendRetryCount = 0;
            this.ws.send(data)
        } else {
            this.sendRetryCount++
            setTimeout(() => { }, this.sendRetryCount * 500)
        }
    }
    // 关闭socket连接
    close() {
        if (this.ws) {
            this.ws.close();
            this.connected = false; // 更新连接状态
            clearInterval(this.heartbeatInterval); // 清除心跳检测定时器
        }
    }
    // 开启心跳检测
    start() {
        this.heartbeatInterval = setInterval(() => {
            this.data = {
                type: "ping"
            };
            this.send(JSON.stringify(this.data));
        }, 5000);
    }
}

注意点:在webSocket.js这样的非Vue组件文件中,this并不指向Vue实例,因此无法直接通过this.$bus来访问事件总线,所以我这用的是vue.$bus.$emit.

在main.js文件中:我的事件总线是挂载在Vue原型上的,那么我在组件中就可以使用this.$bus来访问它

Vue.use(ElementUI);

new Vue({
  router,
  store,
  render: h => h(App),
  beforeCreate() {
    Vue.prototype.$bus = this
  }

}).$mount('#app')

在App.vue文件里的mounted生命周期钩子中执行,在登录成功后调用了SocketService.Instance.connect(this)来连接WebSocket。

<script>
import SocketService from "./utils/websocket";
import { AutoLoginApi } from "@/request/api";
export default {
  mounted() {
    const token = localStorage.getItem("token");
    if (token) {
      AutoLoginApi()
        .then(res => {
          if (token) {
            SocketService.Instance.connect(this);
          }
        })
        .catch(error => {
          console.log("自动登录error", error);
        });
    }
  }
};
</script>

在需要监听数据的页面进行this.$bus.$on监听, this.$bus.$off 移除自定义事件监听器。

<script>

export default {
  data() {
    return {

    };
  },
  beforeDestroy() {
    console.log("销毁");
    this.stopListening();
  },
  created() {
    this.startListening();
  },

  methods: {
    startListening() {
      this.$bus.$on("charge_normal_end", this.on_res_msg_charge_normal_end);
      this.$bus.$on("shots_count_change", this.on_res_msg_shots_count_change);
    },
    stopListening() {
      this.$bus.$off("charge_normal_end", this.on_res_msg_charge_normal_end);
      this.$bus.$off("shots_count_change", this.on_res_msg_shots_count_change);
    },
    on_res_msg_charge_normal_end(data) {
      //在这里处理逻辑
      console.log("输出信息查看", data);
    },
    on_res_msg_shots_count_change(data) {
      console.log("输出信息查看", data);
    }
  }
};
</script>

最后,在退出登录的时候,关闭webSocket

<script>
import SocketService from "@/utils/websocket";
export default {
  data() {
    return {
      
    };
  },
  methods: {
    // 退出登录
    handleCommand(command) {
     // 清除token
        localStorage.removeItem("token");
     // 关闭WebSocket连接
        if (SocketService.Instance.connected) {
          console.log("关闭WebSocket连接");
          SocketService.Instance.ws.close();
        }
    }
  }
};
</script>

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>