vue项目 如何使用websocket
一、介绍业务要求:用户可以看到列表中哪些用户是在线的,能跟在线用户发送消息,并收到即时消息。参考网上一些介绍,根据实战经验进行总结。二、思路1、用户登录以后,进行websocket连接;2、首次登录,获取在线用户信息并渲染,每次接受新消息,出现消息提示,并调用相应方法(根据业务需要);3、退出登录需要断开连接。三、注意1、token过期问题。websocket在连接过程中,token可能过期。解决
一、介绍
业务要求:用户可以看到列表中哪些用户是在线的,能跟在线用户发送消息,并收到即时消息。
参考网上一些介绍,根据实战经验进行总结。
二、思路
1、用户登录以后,进行websocket连接;
2、首次登录,获取在线用户信息并渲染,每次接受新消息,出现消息提示,并调用相应方法(根据业务需要);
3、退出登录需要断开连接。
三、遇到的问题与解决思路
1、vue项目中如何解决在页面刷新以后,websocket关闭导致异常的问题?
把websocket封装在socket.js中,在socket.js中初始化函数,在main.js作为全局引入。每次刷新都会重新连接websocket。
2、发现全局引入以后,在注册页面、以及登录退出以后还在不停调用?
进行token判断。
3、获取websocket消息以后,对数据解析以后存在变量中,如何监听这个变量?
一开始将变量存在window中,但没有找到监听window变量的方法。又试着存在vue.prototype中,也没找到监听方法(有人说可以定义在根组件里用$root访问)。最后用vuex存变量,能成功监听了。
4、将存在vue.prototype中的值作为判断条件来渲染页面的时候,对数值监听,数值更新了,条件改变了,调用$forceUpdate(),但页面不重新渲染?
我的理解是,vue的响应式系统不追踪vue.prototype,所以,我这边是先将vue.prototype中的值赋值给本地变量。用本地变量作为判断条件进行渲染页面。使用this.$nextTick,页面能重新渲染了。
5、如何让消息提示在所有页面都能接收到?
通过bus,把$notify写在全局函数main.js中。
6、如何让用户接收到数据以后,调用某页面的方法?
在main.js中接收到消息以后,通过bus.$emit('refreshApplication'); 发送,在相应的页面的mounted中接收,并调用方法。
bus.$on('refreshApplication', () => {
//按照业务需要调用方法
});
四、实现
1、socket.js
import Vue from 'vue'
import Cookies from 'js-cookie'
import storeOnlineuser from "@/store/modules/onlineUser.js"
import bus from "@/api/bus.js"
import {dateFormatter} from "@/utils/validate"
let socket = null;
let lockReconnet = false; //避免重复连接
const TokenKey = 'USERTOKEN'
const wsUrl = `${window.g.WEBSOCKETPATH}?token=${Cookies.get(TokenKey)}` ;
console.log(wsUrl)
let isReconnet = false;
let globalCallback = null, sendData = null; //把要发送给socket的数据和处理socket返回数据的回调保存起来
//export用以登录后调用 避免在登录页面也调用
export let createSocket = url => { //创建socket
//判断有无token
if(!Cookies.get(TokenKey)){
return false
}
try {
if ('WebSocket' in window) {
socket = new WebSocket(url)
} else if ('MozWebSocket' in window) {
socket = new MozWebSocket(url)
}
Vue.prototype.socket = socket //需要主动关闭的话就可以直接调用this.socket.close()进行关闭,不需要的话这个可以去掉
initSocket()
} catch (e) {
reconnet(url)
}
}
let sendMsg = (data, callback) => { //发送数据,接收数据
if (socket.readyState === 1) {
globalCallback = callback;
sendData = data;
data = JSON.stringify(data);
console.log('发送数据',data)
socket.send(data);
}else {
setTimeout(() => {
console.log(socket, '等待socket链接成功')
sendMsg(data, callback)
}, 3500)
return false
}
socket.onmessage = ev => {
callback && callback(ev)
}
}
let initSocket = () => { //初始化websocket
socket.onopen = () => {
//heartCheck.reset().start() //暂时不需要做心跳检测
if (isReconnet) {//执行全局回调函数
console.log('websocket重新连接了')
isReconnet = false
}
console.log('websocket连接成功')
}
socket.onmessage = (ev) => {
console.log(ev, '连接正常')
//存数据 得考虑会不会被覆盖的问题
let res = JSON.parse(ev.data)
console.log('获得消息',res)
if(res.Type == 'Event' && res.Message.Data.type == '呼叫'){
//获取到呼叫信息 发送信息 其他页面获取到以后 调用相应方法 根据业务需要写
bus.$emit('refreshApplication');
bus.$notify.info({
title: res.Message.Data.type,
dangerouslyUseHTMLString: true,
message: `<strong>${res.Message.Data.sender}</strong>呼叫<strong>${res.Message.Data.receiver}</strong><br>呼叫时间:${dateFormatter(res.SendTime)}`,
duration: 0,
position: 'bottom-right'
});
}
//Type==Chat时Message内列表为在线用户数据列表
else if(res.Type == 'Chat'){
Vue.prototype.onlineUserRes = res.Message // 用来页面遍历显示 也可以直接用store中的数据
storeOnlineuser.state.onlineUser = res.Message //存储在线用户信息
}
//heartCheck.reset().start() //暂时不需要做心跳检测
}
socket.onerror = () => {
console.log('websocket服务出错了');
reconnet(wsUrl)
}
socket.onclose = () => {
console.log('websocket服务关闭了');
reconnet(wsUrl)//防止自动断开
}
}
let reconnet = url => { //重新连接websock函数
//判断有无token
if(!Cookies.get(TokenKey)){
return false
}
if (lockReconnet)
return false
isReconnet = true;
lockReconnet = true
setTimeout(() => {
createSocket(url)
lockReconnet = false
}, 4000)
}
let heartCheck = { //心跳检测
timeout: 60 * 1000,
timeoutObj: null,
serverTimeoutObj: null,
reset() {
clearTimeout(this.timeoutObj)
clearTimeout(this.serverTimeoutObj)
return this;
},
start() {
let that = this;
this.timeoutObj = setTimeout(() => {
//发送数据,如果onmessage能接收到数据,表示连接正常,然后在onmessage里面执行reset方法清除定时器
socket.send('heart check')
this.serverTimeoutObj = setTimeout(() => {
socket.close()
}, that.timeout)
}, this.timeout)
}
}
//避免刷新以后页面没有数据
createSocket(wsUrl)
export default { sendMsg }
2、在main.js引用
//全局发送消息
import socket from '@/utils/mysocket'
Vue.prototype.sendMsg = socket.sendMsg
3、登录的时候调用
createSocket(`${window.g.WEBSOCKETPATH}?token=${getToken()}`)
4、获取实时在线人数。由于在mainJS中已经将消息存在vuex中,所以直接监听。
<template>
<div class="app-container">
<el-table
:data="patList"
max-height="580"
:highlight-current-row="true"
>
<el-table-column prop="userName" label="用户" width="120">
<template slot-scope="scope">
{{scope.row.userName}}
<i class="el-icon-s-opportunity" :class="onlineUser && onlineUser.includes(scope.row.userId) ? 'online' : 'offline'"></i>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import integrated from "@/views/integrated/index"
export default {
name: 'index',
data() {
return {
patList:[{
userName:'章三',
userId:'1'
}],
onlineUser:[],
}
},
watch:{
'$store.state.online.onlineUser':{
immediate:true,
handler(val){
this.$nextTick(()=> {
this.onlineUser = this.onlineUserRes
})
}
}
},
methods: {
},
}
</script>
<style lang="scss" scoped>
.online{
color: forestgreen;
}
.offline{
color:gainsboro;
}
</style>
5、发送消息
export default {
data() {
return {
callData: {
Sender: this.$store.state.user.id,//发送者id
Receiver: "",//接收者id
SendTime: "",//2020-08-06T16:32:22.3224288+08:00
Message: {
Event: "Call",
Data: "",//发送的消息
},
Type: "Event"
},
}
}
call(row){
let call = {
sender : this.$store.state.user.userName,
type:'呼叫',
receiver: row.receiverName
}
//呼叫接受人
this.callData.Receiver = row.userId
this.callData.Message.Data = call
this.$message({
type: 'success',
message: '已呼叫'
});
this.getSocketData(this.callData)
},
getSocketData(data){
this.sendMsg(data,ev=>{
console.log(JSON.parse(ev.data),'发送后回调获取数据')
})
},
}
6、接收消息
import bus from "@/api/bus.js"
mounted(){
//获取main.js的emit
bus.$on('refreshApplication', () => {
//按照业务需要调用方法
this.fetchData()
});
},
7、项目退出的时候调用
//关闭websocket
Vue.prototype.socket.onclose();
更多推荐
所有评论(0)