websocket实现的全双工通信,真真太香了,以下是笔者在使用时,自己封装的一个简易js工具。若需要源码,请移步这里
1 初始化连接
let socket = null; // 连接对象
let linkFailCount = 0; // 连接次数,目前连接三次
let relinkLoading = null;// 重连全屏loading
/**
* @description: 初始化websocket
* @param {*} linkUrl url的地址
* @return {WebSocket} WebSocket对象
* @Author: liuxin
*/
function initWebSocket(linkUrl = "") {
// 正在连接或连接成功
if (socket && (socket.readyState < 2)) {
return socket;
}
// 如果地址由上层传递过来,则使用传递的地址
if (linkUrl) {
Window.apiConfig[process.env.NODE_ENV].wsUrl = linkUrl;
}
const url = Window.apiConfig[process.env.NODE_ENV].wsUrl;
socket = new WebSocket(url) // 创建对象
socket.onerror = webSocketOnError; // 连接错误处理
socket.onclose = closeWebsocket; // 连接关闭处理
socket.onopen = openWebsocket; // 连接打开处理
return socket;
}
2 连接打开回调
/**
* @description: 打开websocket回调函数处理
* @return {*}
* @Author: liuxin
*/
function openWebsocket() {
console.log("WebSocket连接打开...");
linkFailCount = 0;// 打开连接,连接次数改为0
// 加载动画如果开启,则关闭
if (relinkLoading) {
relinkLoading.close();
}
}
3 连接关闭回调
/**
* @description: 关闭websocket回调函数处理
* @return {*}
* @Author: liuxin
*/
function closeWebsocket() {
// 连接关闭时,立刻开启重连机制
if (linkFailCount < 3 && socket && (socket.readyState >= 2)) {
// 开启重连加载动画
relinkLoading = ElLoading.service({
lock: true,
text: `连接关闭了,正在重连,请稍等...`,
})
initWebSocket();
}
}
4 连接错误处理
笔者这里会重连3次,重连的过程给与用户提示,3次之后会提示用户手动刷新
/**
* @description: 连接错误回调函数处理
* @param {*} e 错误对象
* @Author: liuxin
*/
function webSocketOnError(e) {
linkFailCount++;// 连接失败的次数
if (relinkLoading) {
relinkLoading.close(); // 关闭重连加载动画
}
//连接三次
if (linkFailCount < 3) {
initWebSocket();
// 开启重连加载动画
relinkLoading = ElLoading.service({
lock: true,
text: `第${linkFailCount}次连接失败,正在尝试第${linkFailCount + 1}次重新连接,请稍等...`,
})
} else {
ElMessageBox.confirm(
'连接失败,是否尝试刷新?',
'警告',
{
confirmButtonText: '刷新',
cancelButtonText: '取消',
type: 'warning',
"close-on-click-modal": false
}
)
.then((e) => {
if (e == "confirm") {
location.reload();
}
})
}
}
5 数据处理
这里与后端约定的数据返回,加上type作为接口判断依据,因此这里不一定通用。
返回数据接口:{type:"xxx",data:{}}
/**
* @description: 处理websocket返回的数据
* @param {*} res 后端返回的数据
* @return {Object<JSON>}
* @Author: liuxin
*/
function webscoketDealData(res, type) {
const data = JSON.parse(res.data);
if (data.code !== 200) {
ElMessage.error("服务器错误" + data.message || "");
return { type: "error" };
}
const returnData = { type: data.type, data: data.data };
// 打印日志在前端
// if (type && type == data.type) {
// console.log('消息回来了-----', returnData);
// }
return returnData;
}
6 使用示例
const socket = initWebSocket(state.webSocketUrl); // 连接websocket
if (socket) {
// 这个写法会导致多次进入监听事件
socket.addEventListener("message", (scev) => {
console.log(scev.data);
state.returnData.push(scev.data);
});
/* 监听socket关闭 */
socket.addEventListener("close", () => {
state.returnData.push("连接关闭");
});
}
sendMessage("escalator_data_detail", state.params); // 发送消息给后端请求数据
以下是在上述的基础上,为整改页面监听message多次进入的问题,进行的一个优化处理文章来源:https://www.toymoban.com/news/detail-503967.html
7 优化消息处理
7.1 消息存储在store仓库
import store from "@/store";
/**
* @description: 初始化websocket
* @param {*} link url后更上的地址
* @return {WebSocket} WebSocket对象
* @Author: liuxin
*/
function initWebSocket() {
// 正在连接或连接成功
if (socket && (socket.readyState < 2)) {
return socket;
}
const url = Window.apiConfig[process.env.NODE_ENV].wsUrl;
socket = new WebSocket(url)//这里面的this都指向vue
socket.onerror = webSocketOnError;
socket.onclose = closeWebsocket;
socket.onopen = openWebsocket;
socket.onmessage = (res) => {
// console.log("消息回来了-----", res.data);
const data = JSON.parse(res.data);
if (data.code !== 200) {
ElMessage.error("服务器错误:" + data.message || "");
}
store.commit("commitSocketData", data); // 将消息存入仓库中,而后使用则在仓库取,不再监听message事件
};
return socket;
}
7.2 引入仓库存储
import { createStore } from 'vuex'
export default createStore({
state: {
// 用于存储websocket返回的数据 {后端的给定的type:后端的数据}
socketData: {
},
},
getters: {
/**
* @description: 获取webscoket返回的数据
* @return {*}
* @Author: liuxin
*/
getSocketDataByType: (state) => (type) => {
if (Object.hasOwnProperty.call(state, type)) {
return state[type].data;
}
}
},
mutations: {
/**
* @description:
* @param {*} state state对象
* @param {*} commitData {type:"后端的api类型",data:"后端的数据"}
* @return {*}
* @Author: liuxin
*/
commitSocketData(state, commitData) {
// 如果没有数据,则直接返回
if (!commitData || !commitData.type) {
return;
}
state.socketData[commitData.type] = {}; // 初始化一个对象
state.socketData[commitData.type].data = commitData.data; // 添加数据存储
state.socketData[commitData.type].timelyFlag = Date.now(); // 添加时间戳,保证每次后端的数据都能更新成功
},
},
})
7.3 使用说明
直接引入store仓库,监听仓库数据变化文章来源地址https://www.toymoban.com/news/detail-503967.html
import { useStore } from "vuex"; // 引入vuex
const store = useStore(); // 创建store对象
/**
* @description: 监听vuex获取的数据变化,用于展示在前端
* @Author: liuxin
*/
watch(
() => store.state.socketData["escalator_data_detail"], // 这里escalator_data_detail是后端返回的type
(newData) => {
dealData(newData.data); // 处理数据
}
);
7.4编写一个公共发送消息方法
/**
* @description: 发送websocket数据给后端
* @param {String} method 方法
* @param {JSON} params 参数
* @return {Object<JSON>}
* @Author: liuxin
*/
function sendMessage(method, params) {
const _data = {
api: method,
data: {
...params,
}
};
if (socket && socket.readyState == 1) {
// console.log("sendMessage----------", _data);
socket.send(JSON.stringify(_data))
} else {
// 监听socket打开
socket.addEventListener("open", () => {
// console.log("sendMessage -----open-----", _data);
socket.send(JSON.stringify(_data))
});
}
}
到了这里,关于vue3使用websocket简易封装,包含错误重连机制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!