前言
最近有需求要做一个简单业务的 APP 应用,简单考虑选用 uniapp + uview + vue2.x 方案,因为还有web端页面也需要用到 WebSocket ,简单封装了一个适应 web 端的工具,刚开始直接把 web 端的那套代码拿过来用,跑 H5 模式调试在浏览器没有,打包之后问题就出来了,不支持 WebSocket,当时心里咯噔一下,想着这下完了。
冷静下来,百度查一下,原来 uniapp 基于 ECMAScript 扩展了 uni 对象,非 H5 端不支持 window、document、navigator 等浏览器专用对象。uniapp 也实现了 WebSocket ,只是 API 在 uni 对象下。
uni.connectSocket
相关 API 可以直接使用,不过 APP 端所有 vue 页面只能使用一个 websocket 连接。
既然有 API 那就好办了,参考 H5 的封装改改。找了好久,能参考并且比较好的文章不多,那咱也写一段吧。
参考文章
需求目标
使用 ES6 语法封装一个公用的 WebSocket 类,供组件使用。使用类封装没试过是否可以创建多个 WebSocket 连接,不过 Web 端按照这种方式封装是可以创建多个连接。
实现过程
定义
在 utils 目录,新建一个 websocket.js 文件,定义 WebSocket Class,仔细查看 uniapp 文档,调用 uni.connectSocket
接口可以返回一个 SocketTask 对象,有了这个对象之后,剩下的就是 web 端的差不多了,对象里面支持断开自动重连。
如果需要设置重连延迟时间和重连次数可自行扩展属性,目前是写死在代码中。
注意点:
使用 SocketTask
对象,调用存在时序问题,SocketTask.onOpen 要在 uni.connectSocket
回调调用,不然回调里的内容不会返回。
调用
// index.vue
<script>
import WS from "@/utils/websocket.js"
export default {
//...
data() {
return {
ws: null
}
},
onLoad() {
this.ws && this.ws.closeSocket();
this.ws = new WS(`${process.env.VUE_APP_WS_API}/ws/xxx`)
// 发送数据
// this.ws.webSocketSendMsg('发送信息')
this.ws.getWebSocketMsg(data => {
const dataJson = data;
console.log('data', dataJson);
if(typeof(dataJson) == "object") {
console.log("wsObject", dataJson);
}else {
console.log(dataJson);
}
});
},
beforeDestroy() {
this.ws && this.ws.closeSocket();
},
//...
}
</script>
主要代码
因为 WebSocket 接收的可能是字符串或对象,又封装了一个类型判断函数
// @/utils/utils.js
// ...
// 判断字符串是否为JSON格式
export function isJSON(str) {
if (typeof str == 'string') {
try {
var obj = JSON.parse(str);
if (typeof obj == 'object' && obj) {
return true;
} else {
return false;
}
} catch (e) {
// console.log('error:'+str+'!!!'+e);
return false;
}
}
// console.log('It is not a string!')
}
// ...
websocket.js 完整代码
// @/utils/websocket.js
import { isJSON } from "@/utils/utils"
class WebSocketClass {
constructor(url) {
this.lockReconnect = false; // 是否开始重连
this.wsUrl = ""; // ws 地址
this.globalCallback = null; // 回调方法
this.userClose = false; // 是否主动关闭
this.createWebSocket(url);
}
createWebSocket(url) {
if (typeof (uni.connectSocket) === 'undefined') {
this.writeToScreen("您的浏览器不支持WebSocket,无法获取数据");
return false
}
this.wsUrl = url;
try {
// 创建一个this.ws对象,发送、接收、关闭socket都由这个对象操作
let that = this
this.ws = uni.connectSocket({
url: this.wsUrl,
success(data) {
console.log("websocket连接成功");
that.initEventHandle();
},
});
} catch (e) {
this.reconnect(url);
}
}
// 初始化
initEventHandle() {
// 监听WebSocket连接打开成功
this.ws.onOpen(res => {
console.log('WebSocket连接打开');
});
// 连接关闭后的回调函数
this.ws.onClose(() => {
if (!this.userClose) {
this.reconnect(this.wsUrl); //重连
}
});
// 报错时的回调函数
this.ws.onError(() => {
if (!this.userClose) {
this.reconnect(this.wsUrl); //重连
}
});
// 收到服务器数据后的回调函数
this.ws.onMessage(event => {
if(isJSON(event.data)) {
const jsonobject = JSON.parse(event.data)
this.globalCallback(jsonobject)
}else {
this.globalCallback(event.data)
}
});
}
// 关闭ws连接回调
reconnect(url) {
if (this.lockReconnect) return;
this.ws.close();
this.lockReconnect = true; // 关闭重连,没连接上会一直重连,设置延迟避免请求过多
setTimeout(() => {
this.createWebSocket(url);
this.lockReconnect = false;
}, 30000); // 重连延迟时间
}
// 发送信息方法
webSocketSendMsg(msg) {
this.ws && this.ws.send({
data: msg,
success() {
console.log("消息发送成功");
},
fail(err) {
console.log("关闭失败", err)
}
});
}
// 获取ws返回的数据方法
getWebSocketMsg(callback) {
this.globalCallback = callback
}
// 关闭ws方法
closeSocket() {
if (this.ws) {
this.userClose = true;
this.ws.close({
success(res) {
console.log("关闭成功", res)
},
fail(err) {
console.log("关闭失败", err)
}
});
}
}
writeToScreen(massage) {
console.log(massage);
}
}
export default WebSocketClass;
实现结果
实测可用