使用uni-app开发app时遇到mqtt.js不可用的问题

使用uni-app开发app时遇到mqtt.js不可用的问题

1 问题背景

基于 Vue3 版本创建了 uni-app 项目用于开发微信小程序,项目中用到了 mqtt.js(v4.1.0),编译为微信小程序能够正常运行,但是编译为 APP 后,控制台打印出如下错误:

bash 复制代码
TypeError: socketTask.onOpen is not a function

看到 socketTask 后立刻想到了微信小程序,个人以为是 mqtt 连接地址的问题。因为在微信小程序中使用 mqtt.js 时需要将 mqtt 连接地址写为 wx:IP地址:端口号/mqtt,所以把连接地址改为了 ws:IP地址:端口号/mqtt,重新运行后控制台打印出如下错误:

bash 复制代码
TypeError: WS is not a constructor

经过查看源码(node_modules/mqtt/dist/mqtt.js)发现相关代码如下:

js 复制代码
// ... other code ...
var WS = require('ws')

// ... other code ... 

if (isNative && isBrowser) {
  socket = new WS(target, protocols)
} else {
  socket = new WS(target, protocols, options)
}

添加 console.log() 语句查看 WS 变量后发现控制台打印结果为 null,即引入第三方库 ws 失败。

直接在 pages/index/index.vue 中引入 ws 库并打印变量,打印结果如下:

js 复制代码
// pages/index/index.vue
const WS = require('ws')
console.log(WS)
bash 复制代码
# 控制台打印结果
function() {
  throw new Error(
    'ws does not work in the browser. Browser clients must use the native ' + 'WebSocket object'
  );
}

2 引发原因

经查看 ws 库的 Github 主页 发现该库仅在 Node.js 环境下运行。结合提示信息,说明 uni-app 在 APP 端的运行环境和浏览器相关。

随后查询官方文档发现:

vue页面在App端,默认是被系统的webview渲染的(不是手机自带浏览器,是rom的webview)。

也就是说 uni-app 在 APP 端的运行环境是 webview,而 ws 库仅在 Node.js 环境下运行,从而导致通过 require() 引入后为 null。

3 解决方法

既然 uni-app 在 APP 端的运行环境是 webview,而微信小程序的运行环境也是 webview,二者运行环境相似,应该可以共用一个创建 WebSocket 连接的 API。而 mqtt.js 在小程序环境中可以正常运行,所以首先将 mqtt 连接地址改回 wx:IP地址:端口号/mqtt,通过小程序 API wx.connectSocket() 创建 WebSocket 连接。

但是问题又回到了一开始,运行后控制台会打印如下错误信息:

bash 复制代码
TypeError: socketTask.onOpen is not a function

3.1 解决方法1:降低 Vue 版本【推荐⭐】

搜索了很多教程,发现其发布时间多为 2019 ~ 2021,然后想到会不会是 Vue 版本较高导致,尝试降低为 Vue2 版本后重新运行,发现运行成功。

3.2 解决方法2:修改 mqtt.js 源码【推荐⭐⭐⭐⭐⭐】

搜索教程中发现修改源码(node_modules/mqtt/dist/mqtt.js)中的 wx.connectSocket() 也可以解决,解决方法是在调用 wx.connectSocket() 时传入回调函数。

js 复制代码
socketTask = wx.connectSocket({
  url: url,
  protocols: [websocketSubProtocol],
  success() {} // 增加这行代码
})

具体原理可见 uni-app 官方API uni.connectSocket():

对于 uni.connectSocket() API 而言:

如果没有传入 success / fail / complete 参数,则会返回封装后的 Promise 对象;

如果希望返回一个 socketTask 对象,需要至少传入 success / fail / complete 参数中的一个。

所以上面新增的回调函数也可以改为 fail() {} 或 complete() {}。

相关推荐
1-1=015 小时前
Vue 3 模板语
前端·javascript·vue.js
风卿15 小时前
看着AI写代码太孤独了,来欣赏星云吧:一个DSH 装饰插件
前端·javascript
牧艺15 小时前
弹层不用再手算坐标了:CSS Anchor Positioning 从原理到可上线
前端·javascript·css
愚坤16 小时前
前端真有意思,JS还能独立做项目?
前端·javascript·github
李纲明18 小时前
微信小程序 getSystemInfo 提示已废弃、屏幕高度算错胶囊还对不齐?getWindowInfo 和 getDeviceInfo 怎么接才稳
javascript·微信小程序·小程序·小程序开发·devdiy
xx z19 小时前
python 手机版下载(安卓 Python 3 运行环境安装说明)
编程·安卓·软件
FungLeo19 小时前
成为全栈·Vue 管理后台篇·用 fetch 建一层可控的请求内核
前端·javascript·vue.js·http·fetch·formdata·接口封装
用户842981424181019 小时前
ByteJS,让重要的 JavaScript 以字节码运行。
javascript
FungLeo21 小时前
成为全栈·Vue 管理后台篇·Vue Router如何守住多角色后台边界
前端·javascript·vue.js·rbac·vue router·路由守卫·前端权限
FungLeo1 天前
成为全栈·Vue管理后台篇·仪表盘不是几张卡片:组合统计接口与角色视图
前端·javascript·vue.js·rbac·仪表盘·管理后台·tanstack query