当前位置:   article > 正文

Vue 使用 Vue-socket.io 实现即时聊天应用(Vue3连接原理分析)

vue-socket.io

Socket.IO 实现原理(一篇文章让你彻底弄懂即时聊天技术)-CSDN博客文章浏览阅读4次。Socket.IO,支持及时、双向与基于事件的交流。它可以在每个平台、每个浏览器和每个设备上工作,可靠性和速度同样稳定,本文将详细介绍 socket.io、socket.io-client、vue-socket.io三者的关系,Vue3 中使用 Socket.io-client 进行通信的分析过程,快来看看吧~https://blog.csdn.net/weixin_47746452/article/details/134802503?spm=1001.2014.3001.5501        上面的文章是深度分析 Socket.io 的使用原理及底层实现,也针对vue3版本推出了更加简介、模块化的形式实现vue3 的连接、通信,相信我,上面的文章才是大家最想要的。

        而本篇则是针对 Vue-socket.io 的连接调整,都是vue3版本的,大家针对自己的库以及技术选择查看,vue3 的连接都是经过验证的,可以放心使用,过程中有任何问题,可以添加技术讨论群,给大家答疑解惑!


前言:

技术讨论群【522121825】

根据小伙伴的留言,很多人希望能出一个 Vue3 的socket连接,也有小伙伴对 room 的有兴趣,毕竟群聊的实现使用 room 更合理些,而不是广播。今天,记录一下 vue3 版本的Socket连接。

一、新建 Vue3 项目

 二、下载相关依赖

npm i element-plus vue-socket.io --s

  ElementPlus 引入注册使用(与平常一样),但是,socket.io 有些差异,还是按照旧模式引入,有问题我们再思考原因:

2.1 后台服务

【连接篇】中的node服务,可得到如下简介代码:

  1. var app = require("express")();
  2. var http = require("http").Server(app);
  3. var io = require("socket.io")(http, {
  4. allowEIO3: true,
  5. cors: {
  6. origin: "http://localhost:8080",
  7. methods: ["GET", "POST"],
  8. credentials: true,
  9. },
  10. });
  11. http.listen(3000, function () {
  12. console.log("listening on *:3000");
  13. });
  14. io.on("connection", function (socket) {
  15. console.log("a user connected");
  16. });

2.2 前端连接

 2.3 启动项目

 

 看出,现在项目能正常通信了,现在试试触发与接收事件:

2.4 触发与接收事件

  1. io.on("connection", function (socket) {
  2. console.log("a user connected");
  3. socket.emit("welcome", "welcome connect socket Serve.");
  4. socket.on("send", (data) => {
  5. console.log("客户端发送消息:", data);
  6. });
  7. });

按照之前的思路,触发事件,this.$socket.emit('xxx'),

接收事件使用   sockets:{

        welcome(data){ xxxx}

},

但是!

  在 App.vue 中监听事件,并没有收到数据,并且触发事件还报错了!

 2.5 原因分析

究其根源,我们应该清楚认识到,vue2与vue3 的区别。我们应该都听说过一句话,vue3 的实例对象更‘轻’了。这也导致了vue3 的this,不能‘装下’ $socket ,我们将 this 输出如下:

vue2 输出 this:

vue2 输出 this.$socket 这个需要重点关注一下,vue3 的触发事件基于这个!!

 Vue3 输出this:

 vue3 输出this.$socket:

 这样是不是清晰看出,为什么不能使用 this.$socket触发事件与 sockets接收事件了吧。 

 三、vue3 使用socket的原理

3.1 socket对象实例

我们之前都不关注socket本身,只是利用其进行通信,现在看看socket自身:

  1. /* SocketIOClient.Socket, */
  2. const socket = new VueSocketIO({
  3. debug: false, // debug调试,生产建议关闭
  4. connection: "http://localhost:3000",
  5. });
  6. console.log(socket);

输出socket:

 有小伙伴应该看到了,这个实例对象,其实就是 vue2 中的 this.$socket!故而,vue2只不过是将socket实例放在vue实例上,实际处理逻辑的,还是socket实例自身,因此,我们可以使用socket对象来触发和监听事件了。

3.2 socket 触发事件

根据刚才打印的 vue2 中的this.$socket,与vue3中的 socket.io是一样的,故而,(vue2:this.$socket.emit())=  (vue3: socket.io.emit('eventName',someData)) 触发事件:

socket.io.emit("send", "测试 socket.io.emit事件");

 3.3 socket对象监听原生事件

 理论上,socket.io.emit触发方法,应该使用 socket.io.on()监听事件,但事实不行。socket.io.on是有特殊用处的,用于监听原生的默认事件:

  1. // socket.io.on 不能用于监听 node 自定义事件
  2. socket.io.on("welcome", (data) => {
  3. console.log("welcome",data); // 监听自定义事件
  4. });
  5. // 但是可以监听 默认事件
  6. socket.io.on("connect", () => {
  7. console.log("connect"); // 监听 socket 连接事件
  8. });

默认事件有:

3.4 vue-socket.io 源码解析

因为不能直接使用 socket.io.on() 监听事件,我们就需要看看vue2 中是如何实现的,为啥会有 sockets:{},就能监听到事件。根据 vue2 中的 sockets:{subscribe} , 在源码中直接搜索 sockets.subscribe,将相关联的函数抽出,得到如下代码:

  1. var n = {
  2. beforeCreate() {
  3. this.sockets || (this.sockets = {}),
  4. (this.sockets.subscribe = (t, e) => {
  5. this.$vueSocketIo.emitter.addListener(t, e, this);
  6. }),
  7. (this.sockets.unsubscribe = (t) => {
  8. this.$vueSocketIo.emitter.removeListener(t, this);
  9. });
  10. },
  11. mounted() {
  12. this.$options.sockets &&
  13. Object.keys(this.$options.sockets).forEach((t) => {
  14. "subscribe" !== t &&
  15. "unsubscribe" !== t &&
  16. this.$vueSocketIo.emitter.addListener(
  17. t,
  18. this.$options.sockets[t],
  19. this
  20. );
  21. });
  22. },
  23. beforeDestroy() {
  24. this.$options.sockets &&
  25. Object.keys(this.$options.sockets).forEach((t) => {
  26. this.$vueSocketIo.emitter.removeListener(t, this);
  27. });
  28. },
  29. };

以上代码不解读了,不懂的伙伴可以留言讨论哈。

为啥vue原来没有sockets属性,直接加sockets就能监听事件,原理是使用了$options将sockets中的事件,通过this.$vueSocketIo.emitter.addListener()添加到 socket对象上。

 this.$vueSocketIo.emitter.addListener(
            t,
            this.$options.sockets[t],
            this
          );

 模拟this.$vueSocketIo.emitter.addListener()的形式实现事件监听:

3.5 使用emitter.addListner() 监听事件***

  1. /* SocketIOClient.Socket, */
  2. const socket = new VueSocketIO({
  3. debug: false, // debug调试,生产建议关闭
  4. connection: "http://localhost:3000",
  5. });
  6. socket.emitter.addListener("welcome", (data) => {
  7. console.log("addListener", data);
  8. });

 胜利的曙光!!!

但是还是报错了,源码还是使用了 this.$options 的形式处理,故而需要处理一下:

也看了很多博主的方案,说需要修改源码,毕竟我们也找到了源码所在的位置,但是不建议大家这么做,我们只需要基于源码再封装处理一下,就能满足我们的使用了

四、基于源码的 sockets 封装

 4.1 addListner()

源码中无非就是 this.$options 和this我们获取不到,那我们再封装一下,如下(新建 sockets.js):

  1. export const registerSockets = (sockets, proxy) => {
  2. sockets &&
  3. Object.keys(sockets).forEach((t) => {
  4. "subscribe" !== t &&
  5. "unsubscribe" !== t &&
  6. proxy.$socket.emitter.addListener(t, sockets[t], proxy);
  7. });
  8. };

4.2 removeListener()

  1. export const destroySockets = (sockets, proxy) => {
  2. sockets &&
  3. Object.keys(sockets).forEach((t) => {
  4. proxy.$socket.emitter.removeListener(t, proxy);
  5. });
  6. };

基于源码的 sockets 方法处理。

4.3 main.js 处理

  1. import { createApp } from "vue";
  2. import App from "./App.vue";
  3. // 引入 ElementPlus
  4. import ElementPlus from "element-plus";
  5. import "element-plus/dist/index.css";
  6. import { registerSockets, destroySockets } from "./sockets";
  7. // 引入 socket.io
  8. import VueSocketIO from "vue-socket.io";
  9. const app = createApp(App);
  10. /* SocketIOClient.Socket, */
  11. const socket = new VueSocketIO({
  12. debug: false, // debug调试,生产建议关闭
  13. connection: "http://localhost:3000",
  14. });
  15. // 便于在任意位置获取到 socket 对象
  16. app.config.globalProperties.$socket = socket;
  17. // 监听事件
  18. app.config.globalProperties.$addSockets = registerSockets;
  19. // 移除事件
  20. app.config.globalProperties.$removeSockets = destroySockets;
  21. app.use(ElementPlus).mount("#app");

4.4 实现监听事件

在需要监听的页面使用:

  1. import { getCurrentInstance, onMounted, onBeforeUnmount } from "vue";
  2. setup() {
  3. // 获取 当前实例对象
  4. const { proxy } = getCurrentInstance();
  5. // 触发事件
  6. proxy.$socket.io.emit("send", "client send some data to node Serve.");
  7. // 定义监听node事件
  8. const sockets = {
  9. welcome(data) {
  10. console.log(data);
  11. },
  12. };
  13. // 注册 node 事件
  14. onMounted(() => {
  15. proxy.$addSockets(sockets, proxy);
  16. });
  17. // 注销 node 事件
  18. onBeforeUnmount(() => {
  19. proxy.$removeSockets(sockets, proxy);
  20. });
  21. },

能正常接收信息,也能发送消息:

 

 五、总结

总的来看,要深入了解socket的源码,知道其实现方式,基于源码,将vue3的特性结合进去。现在的事件触发与接收,均使用 proxy 实现。

当然,也可以在app中,直接获取子组件的所有 setup 中的sockes,进行统一注册,有更简洁的方式实现,但是原理就是使用vue3 的proxy。

5.1 所有代码如下:

main.js

  1. import { createApp } from "vue";
  2. import App from "./App.vue";
  3. // 引入 ElementPlus
  4. import ElementPlus from "element-plus";
  5. import "element-plus/dist/index.css";
  6. import { registerSockets, destroySockets } from "./sockets";
  7. // 引入 socket.io
  8. import VueSocketIO from "vue-socket.io";
  9. const app = createApp(App);
  10. /* SocketIOClient.Socket, */
  11. const socket = new VueSocketIO({
  12. debug: false, // debug调试,生产建议关闭
  13. connection: "http://localhost:3000",
  14. });
  15. // 便于在任意位置获取到 socket 对象
  16. app.config.globalProperties.$socket = socket;
  17. // 监听事件
  18. app.config.globalProperties.$addSockets = registerSockets;
  19. // 移除事件
  20. app.config.globalProperties.$removeSockets = destroySockets;
  21. app.use(ElementPlus).mount("#app");

sockets.js

  1. export const registerSockets = (sockets, proxy) => {
  2. sockets &&
  3. Object.keys(sockets).forEach((t) => {
  4. "subscribe" !== t &&
  5. "unsubscribe" !== t &&
  6. proxy.$socket.emitter.addListener(t, sockets[t], proxy);
  7. });
  8. };
  9. export const destroySockets = (sockets, proxy) => {
  10. sockets &&
  11. Object.keys(sockets).forEach((t) => {
  12. proxy.$socket.emitter.removeListener(t, proxy);
  13. });
  14. };

App.vue

  1. <template>
  2. <div> app </div>
  3. </template>
  4. <script>
  5. import { getCurrentInstance, onMounted, onBeforeUnmount } from "vue";
  6. export default {
  7. components: { HelloWorldVue },
  8. setup() {
  9. const { proxy } = getCurrentInstance();
  10. const sockets = {
  11. welcome(data) {
  12. console.log(data);
  13. },
  14. };
  15. proxy.$socket.io.emit("send", "client send some data to node Serve.");
  16. onMounted(() => {
  17. proxy.$addSockets(sockets, proxy);
  18. });
  19. onBeforeUnmount(() => {
  20. proxy.$removeSockets(sockets, proxy);
  21. });
  22. return {};
  23. },
  24. };
  25. </script>

优化方案可以自己研究一下,基于 vue3 的socket rooms demo 下期更新。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/784818
推荐阅读
相关标签
  

闽ICP备14008679号