欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 前端技术 > vue >内容正文

vue

Vue+WebSocket-实现多人聊天室

发布时间:2024/3/24 vue 60 豆豆
生活随笔 收集整理的这篇文章主要介绍了 Vue+WebSocket-实现多人聊天室 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

在前端中 WebSocket 是H5新增的对象

主要作用有:实时通讯  长连接  双向传输  后端主动推送数据

websocket实例的主要事件

前端:

直接new 一个实例

const ws = new WebSocket("ws://localhost:9100");

大部分浏览器已经支持 WebSocket 对象  协议格式为ws(不是 file http)

主要事件:

open建立链接
close断开链接
error发生错误
message发送消息给后端

后端:

使用node 来简单搞一个 本地服务、后端要依赖第三方包使用websocket 这里以ws为例

npm i ws@8.10.0

引入ws 创建服务 配置端口和前端一致

const ws = require('ws')

const wss= new ws.Server({port:9100})

主要事件:

open建立链接
close断开链接
error发生错误

connection

message

有客户端连接上 

接收到客户端发送来的消息

一般 message事件在 connection里面

案例流程梳理

  • 首先登录 选择 头像 起昵称(不能和聊天室内已经有的的重复)
  • 进入聊天室 发送欢迎信息  显示在线人数+1
  • 发消息时 后端会自动转发给其他连接上的客户端
  • 退出的时候 释放 昵称的命名空间 
  • 登录页面展示: 

     样式部分省略...

    使用双向绑定获取 用户输入的昵称和 选择的头像  头像v-for渲染数据  点击时currentIndex修改为当前的索引 根据索引 增加高亮边框和 选择此数据传给下一步

    <input type="text" placeholder="请输入发言昵称" v-model="nickname" id="input" /><ul class="avatar"><li v-for="(aa,index) in avatar_list":key="index":class="{curr : currentIndex===index}"@click="bianse(index)"><img :src="aa" alt=""></li>

    验证输入用户的长度要在1-9位

    想服务器发起请求 存放昵称(禁止其他人使用)

    收集数据保存到  localStorage 中 进行下一步

    methods:{defind(){if (this.nickname.length < 1) {return alert("请输入昵称");}if (this.nickname.length > 9) {return alert("输入昵称过长");}this.$http.get(`/login/${this.nickname}`).then(res => {if(res.data.status === 1){localStorage.setItem("nickname", this.nickname);localStorage.setItem("avatar", this.avatar_list[this.currentIndex]);this.$router.push('/about')}else{return alert("昵称已被占用");}})},bianse(index){this.currentIndex = index}}

    聊天室页面

     预留组件中需要的数据  进入组件和挂载元素 生命周期中进行对应的操作

    进入到页面 简易判断 前一步保存到 localStorage 的数据是否存在 如果不存在自动返回登录页面重新设置  防止用户跳过登录直接进入到 聊天室 没有昵称导致的一系列错误   这一步也可以使用 导航守卫来实现

    data(){return {nickname:'', // 用户的昵称message:'', // 用户发送的消息record:[], // 消息记录数组ws:null, // ws 实例 预留变量user_list:[] // 实时在线人数列表}},created() {this.nickname = localStorage.getItem("nickname")if (!this.nickname) {return this.$router.push('/');}},

    mounted 生命周期中 创建 WebSocket实例

    添加 ws相关事件 这里只需要

    open 连接上ws服务器端了 发送欢迎消息

    message 接收到服务器返回来的数据 渲染到页面 聊天信息部分 并判断是新进入的用户发送的消息 还是老用户发送的消息 如果是新用户 就添加到左侧在线列表 老用户此步骤忽略

    业务流程:连接上后端发送欢迎消息 =》后端接收消息 返回给每个客户端  =》 客户端接收到服务端发来的消息 渲染到 消息列表 并且根据条件 渲染左侧在线列表

    mounted() {this.ws = new WebSocket("ws://localhost:9100");this.ws.addEventListener("open", () => {this.ws.send(JSON.stringify({user: this.nickname,avatar:localStorage.getItem('avatar'),dateTime: this.nowTimeFormatChinese(new Date()),message:'欢迎 ' +this.nickname + ' 来到聊天室',}));});this.ws.addEventListener("message", (e) => {const data = JSON.parse(e.data)this.record.push(data);const flag = this.user_list.filter(x => x.user === data.user)if(flag.length === 0){this.user_list.push(data);}});},

    点击发送按钮  组织好数据 ws.send 发送给服务端 

    发送消息 返回渲染之后 滚动跳滚到最新消息处

              this.$refs.lists.scrollTop += 100

              // window.scrollTo(0, document.body.scrollHeight);

     还有格式化时间的方法

    methods:{send(){if (!this.message.trim().length) {return alert("请输入内容");}this.ws.send(JSON.stringify({user: this.nickname,avatar:localStorage.getItem('avatar'),dateTime: this.nowTimeFormatChinese(new Date()),message: this.message,}));this.message = "";setTimeout(()=>{this.$refs.lists.scrollTop += 100// window.scrollTo(0, document.body.scrollHeight);},100)},padZero(n){return n > 9 ? n : "0" + n;},nowTimeFormatChinese(riqi){let hour = this.padZero(riqi.getHours()),min = this.padZero(riqi.getMinutes()),sec = this.padZero(riqi.getSeconds())return hour + "时" + min + "分" + sec + "杪";}},

    离开页面(销毁组件)时 清楚自己的昵称 ---左侧的在线列表 和 服务器端的命名空间

    deactivated(){const index = this.user_list.findIndex(x => x.user === this.nickname)this.user_list.splice(index, 1)this.$http.get(`/loginout/${this.nickname}`)},

     聊天室页面完整模板(样式省略):

    渲染消息列表时 判断是不是自己所发的消息 返回来的user === 自己的nickname

    是的话添加  meSay 样式  右侧显示 作为区分

    <template><div class="about"><ul id="list" ref="lists"><li v-for="(n,index) in record" :key="index" :class="{meSay : n.user === nickname}"> <div><div class="cow"><img :src="n.avatar" alt="" class="avatar"><p class="ppp"><span>{{n.user}}</span><br><span>{{n.dateTime}}</span></p></div><div class="nei">{{n.message}}</div> </div></li></ul><div class="bottom"><div class="people"><h3>当前在线人数:{{this.user_list.length}}</h3><div class="user_list" v-for="n in user_list" :key="n.user"><img :src="n.avatar" alt="">{{n.user}}</div></div><textarea id="message" v-model="message" @keyup.enter="send"></textarea><button id="send" @click="send">发送</button></div></div> </template>

    后端完整代码

    ws部分比较简单  监听链接 和 接收消息的事件  出发了就遍历所有链接的客户端 把数据原封不动的发送出去

    const ws = require('ws')const wss= new ws.Server({port:9100})wss.on('connection',(client)=>{ // clent 这个客户端链接了client.on('message',(msg)=>{ // 并且发来了数据const radio = msg.toString() // 数据转换格式防止乱码wss.clients.forEach(e =>{ // 遍历再原封不动发送给每个链接的客户端e.send(radio)})}) })

    管理昵称命名空间的端口

    引入express 快速搭建本地服务器

    npm i express@4

    使用中间件 解决跨域问题

    创建 activeUser 数组储存 已在线的用户昵称、登录时发送请求携带 nickname req.params 获取路径中的形参 判断在 activeUser 中是否存在 如果存在添加失败 不存在 添加进去 这样保证昵称不重复、 注销时发送请求 携带nickname 在activeUser 查找到 并且 删除它

    // 导入 express 模块 const express = require('express') // 创建 express 的服务器实例 const app = express()// 这样也可以解决跨域问题 app.use(function(req,res,next){// 第二个 * 代表通配符 也可以指定具体的网站 http://www.wsg3096.comconst contentType = 'application/json; charset=utf-8'res.setHeader('Content-Type',contentType)res.setHeader('Access-Control-Allow-Origin','*')// 后面的也可以用通配符res.setHeader('Access-Control-Allow-Methods','OPTIONS,GET,PUT,POST,DELETE')// 设置其他的请求头res.setHeader('Access-Control-Allow-Headers','Content-Type','X-Custom-Header')next() })const activeUser = []// 登录的 API 接口 app.get('/api/login/:nickname', (req, res) => {const nickname = req.params.nicknameconst find = activeUser.find(x => x=== nickname)if(find){return res.send({status:0,msg:'用户昵称已经被占用'})}else{activeUser.push(nickname)return res.send({activeUser,status:1,msg:'成功进入聊天室队列'})} })// 注销的接口 app.get('/api/loginout/:nickname', (req, res) => {const nickname = req.params.nicknameconst index = activeUser.findIndex(x => x=== nickname)activeUser.splice(index,1)return res.send({activeUser,status:200,msg: `成功释放${nickname}的命名空间`}) })// 调用 app.listen 方法,指定端口号并启动web服务器 app.listen(7777, function () {console.log('Express server running at http://127.0.0.1:7777') })

    总结

    以上是生活随笔为你收集整理的Vue+WebSocket-实现多人聊天室的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。