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

news2024/11/17 21:29:45

目录

前言:

一、新建 Vue3 项目

 二、下载相关依赖

2.1 后台服务

2.2 前端连接

 2.3 启动项目

2.4 触发与接收事件

 2.5 原因分析

 三、vue3 使用socket的原理

3.1 socket对象实例

3.2 socket 触发事件

 3.3 socket对象监听原生事件

3.4 vue-socket.io 源码解析

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

四、基于源码的 sockets 封装

 4.1 addListner()

4.2 removeListener()

4.3 main.js 处理

4.4 实现监听事件

 五、总结

5.1 所有代码如下


前言:

技术讨论群【522121825】

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

一、新建 Vue3 项目

 二、下载相关依赖

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

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

2.1 后台服务

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

var app = require("express")();
var http = require("http").Server(app);
var io = require("socket.io")(http, {
  allowEIO3: true,
  cors: {
    origin: "http://localhost:8080",
    methods: ["GET", "POST"],
    credentials: true,
  },
});

http.listen(3000, function () {
  console.log("listening on *:3000");
});

io.on("connection", function (socket) {
  console.log("a user connected");
});

2.2 前端连接

 2.3 启动项目

 

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

2.4 触发与接收事件

io.on("connection", function (socket) {
  console.log("a user connected");

  socket.emit("welcome", "welcome connect socket Serve.");

  socket.on("send", (data) => {
    console.log("客户端发送消息:", data);
  });
});

按照之前的思路,触发事件,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自身:

/* SocketIOClient.Socket, */
const socket = new VueSocketIO({
  debug: false, // debug调试,生产建议关闭
  connection: "http://localhost:3000",
});
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是有特殊用处的,用于监听原生的默认事件:

// socket.io.on 不能用于监听 node 自定义事件
socket.io.on("welcome", (data) => {
  console.log("welcome",data); // 监听自定义事件
});

// 但是可以监听 默认事件
socket.io.on("connect", () => {
  console.log("connect"); // 监听 socket 连接事件
});

默认事件有:

3.4 vue-socket.io 源码解析

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

var n = {
  beforeCreate() {
    this.sockets || (this.sockets = {}),
      (this.sockets.subscribe = (t, e) => {
        this.$vueSocketIo.emitter.addListener(t, e, this);
      }),
      (this.sockets.unsubscribe = (t) => {
        this.$vueSocketIo.emitter.removeListener(t, this);
      });
  },
  mounted() {
    this.$options.sockets &&
      Object.keys(this.$options.sockets).forEach((t) => {
        "subscribe" !== t &&
          "unsubscribe" !== t &&
          this.$vueSocketIo.emitter.addListener(
            t,
            this.$options.sockets[t],
            this
          );
      });
  },
  beforeDestroy() {
    this.$options.sockets &&
      Object.keys(this.$options.sockets).forEach((t) => {
        this.$vueSocketIo.emitter.removeListener(t, this);
      });
  },
};

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

为啥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() 监听事件***

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

socket.emitter.addListener("welcome", (data) => {
  console.log("addListener", data);
});

 胜利的曙光!!!

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

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

四、基于源码的 sockets 封装

 4.1 addListner()

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

export const registerSockets = (sockets, proxy) => {
  sockets &&
    Object.keys(sockets).forEach((t) => {
      "subscribe" !== t &&
        "unsubscribe" !== t &&
        proxy.$socket.emitter.addListener(t, sockets[t], proxy);
    });
};

4.2 removeListener()

export const destroySockets = (sockets, proxy) => {
  sockets &&
    Object.keys(sockets).forEach((t) => {
      proxy.$socket.emitter.removeListener(t, proxy);
    });
};

基于源码的 sockets 方法处理。

4.3 main.js 处理

import { createApp } from "vue";
import App from "./App.vue";

// 引入 ElementPlus
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import { registerSockets, destroySockets } from "./sockets";

// 引入 socket.io
import VueSocketIO from "vue-socket.io";

const app = createApp(App);

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

// 便于在任意位置获取到 socket 对象
app.config.globalProperties.$socket = socket;
// 监听事件
app.config.globalProperties.$addSockets = registerSockets;
// 移除事件
app.config.globalProperties.$removeSockets = destroySockets;

app.use(ElementPlus).mount("#app");

4.4 实现监听事件

在需要监听的页面使用:

import { getCurrentInstance, onMounted, onBeforeUnmount } from "vue";
 setup() {
    // 获取 当前实例对象
    const { proxy } = getCurrentInstance();

    // 触发事件
    proxy.$socket.io.emit("send", "client send some data to node Serve.");

    // 定义监听node事件
    const sockets = {
      welcome(data) {
        console.log(data);
      },
    };

    // 注册 node 事件
    onMounted(() => {
      proxy.$sockets(sockets, proxy);
    });

    // 注销 node 事件
    onBeforeUnmount(() => {
      proxy.$removeSockets(sockets, proxy);
    });
  },

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

 

 五、总结

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

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

5.1 所有代码如下:

main.js

import { createApp } from "vue";
import App from "./App.vue";

// 引入 ElementPlus
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import { registerSockets, destroySockets } from "./sockets";

// 引入 socket.io
import VueSocketIO from "vue-socket.io";

const app = createApp(App);

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

// 便于在任意位置获取到 socket 对象
app.config.globalProperties.$socket = socket;
// 监听事件
app.config.globalProperties.$addSockets = registerSockets;
// 移除事件
app.config.globalProperties.$removeSockets = destroySockets;

app.use(ElementPlus).mount("#app");

sockets.js

export const registerSockets = (sockets, proxy) => {
  sockets &&
    Object.keys(sockets).forEach((t) => {
      "subscribe" !== t &&
        "unsubscribe" !== t &&
        proxy.$socket.emitter.addListener(t, sockets[t], proxy);
    });
};

export const destroySockets = (sockets, proxy) => {
  sockets &&
    Object.keys(sockets).forEach((t) => {
      proxy.$socket.emitter.removeListener(t, proxy);
    });
};

App.vue

<template>
  <div> app </div>
</template>

<script>
import { getCurrentInstance, onMounted, onBeforeUnmount } from "vue";

export default {
  components: { HelloWorldVue },
  setup() {
    const { proxy } = getCurrentInstance();

    const sockets = {
      welcome(data) {
        console.log(data);
      },
    };

    proxy.$socket.io.emit("send", "client send some data to node Serve.");

    onMounted(() => {
      proxy.$addSockets(sockets, proxy);
    });

    onBeforeUnmount(() => {
      proxy.$removeSockets(sockets, proxy);
    });

    return {};
  },
};
</script>

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/409713.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

宝塔面板安装部署Vue项目,Vue项目从打包到上线

前期准备 1.宝塔面板已经成功安装到服务器 2.vue项目已经成功开发完成 开始 在宝塔面板中选择PHP项目添加站点&#xff0c;站点PHP版本设置为纯静态&#xff0c;输入域名或者IP 这是后你会获得一个网站文件目录 点击根目录进入目录后&#xff0c;若你的Vue项目么有打包好需…

可视化大屏的几种屏幕适配方案,总有一种是你需要的

假设我们正在开发一个可视化拖拽的搭建平台&#xff0c;可以拖拽生成工作台或可视化大屏&#xff0c;或者直接就是开发一个大屏&#xff0c;首先必须要考虑的一个问题就是页面如何适应屏幕&#xff0c;因为我们在搭建或开发时一般都会基于一个固定的宽高&#xff0c;但是实际的…

海康摄像头web无插件3.2,vue开发,Nginx代理IIS服务器

在vue中实现海康摄像头播放&#xff0c;采用海康web无插件3.2开发包&#xff0c;采用Nginx代理IIS服务器实现&#xff1b; 1 摄像头要求&#xff0c;支持websocket 2 Nginx反向代理的结构 3 vue前端显示视频流代码 参考地址&#xff1a; https://blog.csdn.net/Vslong/artic…

【Vue全家桶】新一代的状态管理--Pinia

&#x1f373;作者&#xff1a;贤蛋大眼萌&#xff0c;一名很普通但不想普通的程序媛\color{#FF0000}{贤蛋 大眼萌 &#xff0c;一名很普通但不想普通的程序媛}贤蛋大眼萌&#xff0c;一名很普通但不想普通的程序媛&#x1f933; &#x1f64a;语录&#xff1a;多一些不为什么的…

【微信小程序】一文带你了解数据绑定、事件绑定以及事件传参、数据同步

&#x1f41a;作者简介&#xff1a;苏凉&#xff08;专注于网络爬虫&#xff0c;数据分析&#xff0c;正在学习前端的路上&#xff09; &#x1f433;博客主页&#xff1a;苏凉.py的博客 &#x1f310;系列专栏&#xff1a;小程序开发基础教程 &#x1f451;名言警句&#xff1…

项目完成后的打包流程(超级详细)

* 打包&#xff1a; * 将多个文件压缩合并成一个文件 * 语法降级 * less sass ts 语法解析, 解析成css * .... * 打包后&#xff0c;可以生成&#xff0c;浏览器能够直接运行的网页 > 就是需要上线的源码&#xff01; 最简单的打包流程&#xff1a; 首先我们项目…

vue项目实现前端预览word和pdf格式文件

最近做vue项目遇到一个需求&#xff0c;就是前端实现上传word或pdf文件后&#xff0c;后端返回文件对应的文件流&#xff0c;前端需要在页面上展示出来。word预览简单一些&#xff0c;pdf预览我试过pdfjs,vue-pdf总是报各种奇奇怪怪的bug,但最终总算解决了问题&#xff0c;先看…

HttpServletResponse 类

a)HttpServletResponse 类的作用 HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来&#xff0c;Tomcat 服务器都会创建一个 Response 对象传 递给 Servlet 程序去使用。HttpServletRequest 表示请求过来的信息&#xff0c;HttpServletResponse 表示所有响应…

解决TypeError: Cannot read properties of null (reading ‘xxx‘)的错误

文章目录1. 文章目录2. 分析问题3. 解决错误4. 问题总结1. 文章目录 今天测试小姐姐&#xff0c;在测试某页面时&#xff0c;报出如下图的错误&#xff1a; TypeError: Cannot read properties of null (reading type)at w (http://...xxx.../assets/index.a9f96e7f.js:1052:19…

前端基础,超全html常用标签大汇总

<html></html>标签&#xff0c;整个html文件都会放在html标签里面 <head></head>标签&#xff0c;表示网页的头部信息&#xff0c;一般是为浏览器提供对应的网站需要的相关信息&#xff0c;浏览器中是不会显示的&#xff1b;比如&#xff1a;标题titl…

vue中PC端使用高德地图 -- 实现搜索定位、地址标记、弹窗显示定位详情

PC端高德地图使用步骤&#xff1a; 1、注册并登录高德开放平台获取 2、安装高德依赖&#xff08;amap-jsapi-loader&#xff09; 3、初始化地图 4、首次打开地图获取当前定位并标记 5、根据已有地址自动定位到指定地址并标记 6、新增、清除标记及自定义信息窗体 7、鼠标点击地…

iframe嵌套其它网站页面及相关知识点详解

在开发过程中会遇到需要 在一个页面中嵌套另外一个页面&#xff0c;就要使用到框架 标签&#xff0c;然后指定src就可以了。 基本语法&#xff1a; <iframe src"需要展示的网站页面的URL"></iframe>用法举例&#xff1a; <!DOCTYPE html> <h…

css ::before ::after 添加伪元素不生效

需求&#xff1a;想用伪元素来写蓝色小标 HTML结构&#xff1a; <div> <span class"course-configname">教程配置</span> </div> 1.一开始这样写&#xff1a;&#xff08;不生效&#xff09; .course-configname::before{content: ;width…

vue3.0-axios拦截器、proxy跨域代理

目录 1. vue-cli 1&#xff09;vue-cli 2&#xff09;安装vue-cli ①解决Windows PowerShell不识别vue命令的问题 3&#xff09;创建项目 4&#xff09;基于vue ui创建vue项目 5&#xff09;基于命令行创建vue项目 2. 组件库 1&#xff09;vue组件库 2&#xff09;v…

Three.js - 透视相机(PerspectiveCamera)(三)

简介 在three.js中&#xff0c;摄像机的作用就是不断的拍摄我们创建好的场景&#xff0c;然后通过渲染器渲染到屏幕中。想通过不同的角度观看场景&#xff0c;就需要修改摄像机的位置来拍摄场景。本文详细介绍的是透视相机&#xff08;PerspectiveCamera&#xff09; 它是用来…

React Hooks(钩子函数)

React Hooks什么是Hooks?UseState()useReducer()useContext()useEffect()useRef()自定义钩子函数React Hooks中可以对性能进行优化的函数useMemo()useCallback()useMemo()和useCallback()的区别什么是Hooks? 首先&#xff1a;React的组件创建方式&#xff0c;一种是类组件&a…

划水日常(16.5)关于出版图书有偿征集书名 ~

关于摸鱼日常已经断更两个月了&#xff0c;前段时间一直忙在项目上&#xff0c;再加上搭了个网站&#xff0c;你要说有事儿吧&#xff0c;其实事儿也不多。你要说没事儿吧&#xff0c;我就是不更。原因其实很简单&#xff0c;我懒&#xff01; 可直接跳过目录一&#xff0c;直至…

web自动化测试入门篇02——selenium安装教程

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

授予渔,从0开始搭建一个自己想要的网页

文章目录视频展示&#xff1a;张娜英网页一.开始阶段1.1考虑出基本的架构1.2填充思路1.3前提准备二.实现阶段2.1导航栏实现2.2点击切换视频2.3 左右特效2.4照片墙2.5视频轮播2.6底部2.7点击切换全局变量3.总结全部代码&#xff1a;☀️作者简介&#xff1a;大家好我是言不及行y…

【实战】React 必会第三方插件 —— Cron 表达式生成器(qnn-react-cron)

文章目录一、引子二、配置使用1.安装2.使用&#xff08;1&#xff09;直接调用&#xff08;2&#xff09;赋值到表单&#xff08;Form&#xff09;&#xff08;3&#xff09;自定义功能按钮&#xff08;4&#xff09;隐藏指定 Tab&#xff08;5&#xff09;其他三、常见问题及解…