在做微信小程序或 H5 对接 MQTT 服务时,你是不是踩过这些坑:
onOpen is not a function报错闪退task not found连接异常TextEncoder/TextDecoder 未定义兼容性问题- 中文消息收发乱码
- 小程序和 H5 无法一套代码兼容运行
别再挨个排查填坑了!我整理了一款MQTT 终极兼容版(wx + H5) ,完美解决以上所有问题,且已固定对接 wss://mq.dtszbj.com/mqtt 服务,开箱即用!
一、完整兼容版 JS 代码(mqtt-wx.js)
直接复制下方代码,保存为 mqtt-wx.js 文件即可使用,无需额外依赖!
/**
* mqtt-wx.js 终极兼容版(写死wss://mq.dtszbj.com/mqtt)
* 适配:微信小程序(wx) + H5(window),解决 onOpen 不是函数、task not found、TextEncoder 未定义、中文乱码等问题
*/
; (function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.mqtt = factory());
}(this, (function () {
'use strict';
// 环境检测:区分小程序/H5
var isMiniProgram = typeof wx !== 'undefined' && typeof wx.connectSocket === 'function';
var isH5 = !isMiniProgram && typeof window !== 'undefined' && typeof window.WebSocket === 'function';
// 写死核心配置:固定为wss://mq.dtszbj.com/mqtt
var defaultOptions = {
timeout: 60 * 1000,
keepalive: 60,
clean: true,
reconnectPeriod: 1000,
clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8),
protocolId: 'MQTT',
protocolVersion: 4,
encoding: 'utf8',
protocol: 'wss', // 固定wss协议
host: 'mq.dtszbj.com', // 固定主机地址
port: 443, // wss默认端口443
};
// MQTT报文类型
var PacketType = {
CONNECT: 1,
CONNACK: 2,
PUBLISH: 3,
PUBACK: 4,
PUBREC: 5,
PUBREL: 6,
PUBCOMP: 7,
SUBSCRIBE: 8,
SUBACK: 9,
UNSUBSCRIBE: 10,
UNSUBACK: 11,
PINGREQ: 12,
PINGRESP: 13,
DISCONNECT: 14
};
// 手动字符串转UTF-8 Uint8Array(替代TextEncoder,支持中文)
function stringToUint8Array(str) {
var arr = [];
var len = str.length;
for (var i = 0; i < len; i++) {
var charCode = str.charCodeAt(i);
// 单字节 (0x00-0x7F)
if (charCode <= 0x7F) {
arr.push(charCode);
}
// 双字节 (0x80-0x7FF)
else if (charCode <= 0x7FF) {
arr.push(0xC0 | (charCode >> 6));
arr.push(0x80 | (charCode & 0x3F));
}
// 三字节 (0x800-0xFFFF)
else if (charCode <= 0xFFFF) {
arr.push(0xE0 | (charCode >> 12));
arr.push(0x80 | ((charCode >> 6) & 0x3F));
arr.push(0x80 | (charCode & 0x3F));
}
// 四字节 (0x10000-0x10FFFF)
else {
arr.push(0xF0 | (charCode >> 18));
arr.push(0x80 | ((charCode >> 12) & 0x3F));
arr.push(0x80 | ((charCode >> 6) & 0x3F));
arr.push(0x80 | (charCode & 0x3F));
}
}
return new Uint8Array(arr);
}
// 手动Uint8Array转UTF-8字符串(修复中文乱码,替代TextDecoder)
function uint8ArrayToString(buf, encoding) {
if (encoding !== 'utf8' && encoding !== 'utf-8') {
console.warn('仅支持UTF-8编码,已自动切换');
}
// UTF-8解码核心逻辑
var str = '';
var i = 0;
var len = buf.length;
while (i < len) {
var byte = buf[i];
// 单字节字符 (0x00-0x7F)
if (byte < 0x80) {
str += String.fromCharCode(byte);
i++;
}
// 双字节字符 (0xC0-0xDF)
else if (byte >= 0xC0 && byte <= 0xDF && i + 1 < len) {
var byte2 = buf[i + 1];
var charCode = ((byte & 0x1F) << 6) | (byte2 & 0x3F);
str += String.fromCharCode(charCode);
i += 2;
}
// 三字节字符 (0xE0-0xEF)
else if (byte >= 0xE0 && byte <= 0xEF && i + 2 < len) {
var byte2 = buf[i + 1];
var byte3 = buf[i + 2];
var charCode = ((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F);
str += String.fromCharCode(charCode);
i += 3;
}
// 四字节字符 (0xF0-0xF7)
else if (byte >= 0xF0 && byte <= 0xF7 && i + 3 < len) {
var byte2 = buf[i + 1];
var byte3 = buf[i + 2];
var byte4 = buf[i + 3];
var charCode = ((byte & 0x07) << 18) | ((byte2 & 0x3F) << 12) | ((byte3 & 0x3F) << 6) | (byte4 & 0x3F);
str += String.fromCharCode(charCode);
i += 4;
}
// 无效字节,跳过
else {
str += '�'; // 替换未知字符
i++;
}
}
return str;
}
// 剩余长度编码
function encodeRemainingLength(length) {
var buffer = [];
do {
var byte = length % 128;
length = Math.floor(length / 128);
if (length > 0) {
byte |= 0x80;
}
buffer.push(byte);
} while (length > 0);
return new Uint8Array(buffer);
}
// 剩余长度解码
function decodeRemainingLength(buffer, offset) {
var length = 0;
var multiplier = 1;
var byte;
do {
byte = buffer[offset++];
length += (byte & 0x7F) * multiplier;
multiplier *= 128;
if (multiplier > 128 * 128 * 128) {
throw new Error('Remaining length is too large');
}
} while ((byte & 0x80) !== 0);
return { length: length, offset: offset };
}
// 构造CONNECT报文(修复重复定义connectFlags,无TextEncoder依赖)
function createConnectPacket(options) {
var clientId = options.clientId;
var clean = options.clean;
var keepalive = options.keepalive;
var protocolId = options.protocolId;
var protocolVersion = options.protocolVersion;
// 连接标志(仅声明一次,显式清零遗嘱相关位)
var connectFlags = 0x00;
if (clean) connectFlags |= 0x02;
var connectFlagsBuf = new Uint8Array([connectFlags]);
// 手动编码协议名
var protoNameBuf = stringToUint8Array(protocolId);
var protoVerBuf = new Uint8Array([protocolVersion]);
// 心跳间隔(2字节)
var keepaliveBuf = new Uint8Array([(keepalive >> 8) & 0xFF, keepalive & 0xFF]);
// 客户端ID(带长度前缀)
var clientIdBuf = stringToUint8Array(clientId);
var clientIdLenBuf = new Uint8Array([(clientIdBuf.length >> 8) & 0xFF, clientIdBuf.length & 0xFF]);
// 拼接可变头
var variableHeaderParts = [
new Uint8Array([protoNameBuf.length >> 8, protoNameBuf.length & 0xFF]),
protoNameBuf,
protoVerBuf,
connectFlagsBuf,
keepaliveBuf
];
var variableHeaderLength = 0;
variableHeaderParts.forEach(part => variableHeaderLength += part.length);
var variableHeader = new Uint8Array(variableHeaderLength);
var offset = 0;
variableHeaderParts.forEach(part => {
variableHeader.set(part, offset);
offset += part.length;
});
// 拼接有效载荷
var payload = new Uint8Array([...clientIdLenBuf, ...clientIdBuf]);
// 计算剩余长度并编码
var remainingLength = variableHeader.length + payload.length;
var remainingLengthBuf = encodeRemainingLength(remainingLength);
// 拼接最终报文
var packetParts = [
new Uint8Array([PacketType.CONNECT << 4]),
remainingLengthBuf,
variableHeader,
payload
];
var totalLength = 0;
packetParts.forEach(part => totalLength += part.length);
var packet = new Uint8Array(totalLength);
offset = 0;
packetParts.forEach(part => {
packet.set(part, offset);
offset += part.length;
});
return packet;
}
// 构造SUBSCRIBE报文(无TextEncoder依赖)
function createSubscribePacket(topic, packetId) {
var fixedHeader = new Uint8Array([(PacketType.SUBSCRIBE << 4) | 0x02]);
var packetIdBuf = new Uint8Array([(packetId >> 8) & 0xFF, packetId & 0xFF]);
// 手动编码主题
var topicBuf = stringToUint8Array(topic);
var topicLenBuf = new Uint8Array([(topicBuf.length >> 8) & 0xFF, topicBuf.length & 0xFF]);
var qosBuf = new Uint8Array([0x00]);
// 拼接有效载荷
var payloadParts = [topicLenBuf, topicBuf, qosBuf];
var payloadLength = 0;
payloadParts.forEach(part => payloadLength += part.length);
var payload = new Uint8Array(payloadLength);
var offset = 0;
payloadParts.forEach(part => {
payload.set(part, offset);
offset += part.length;
});
// 计算剩余长度并编码
var remainingLength = packetIdBuf.length + payload.length;
var remainingLengthBuf = encodeRemainingLength(remainingLength);
// 拼接最终报文
var packetParts = [
fixedHeader,
remainingLengthBuf,
packetIdBuf,
payload
];
var totalLength = 0;
packetParts.forEach(part => totalLength += part.length);
var packet = new Uint8Array(totalLength);
offset = 0;
packetParts.forEach(part => {
packet.set(part, offset);
offset += part.length;
});
return packet;
}
// 构造PUBLISH报文由prototype方法实现
// 强制返回固定地址(写死后无需解析动态URL)
function parseUrl(url) {
return {
protocol: 'wss',
host: 'mq.dtszbj.com',
port: 443,
};
}
// MQTT客户端类
function MqttClient(options) {
var connectOptions = typeof options === 'string' ? parseUrl(options) : options;
this.options = Object.assign({}, defaultOptions, connectOptions);
// 跨端Socket实例(小程序:socketTask,H5:WebSocket)
this.socket = null;
this.socketCreated = false;
this.connected = false;
this.disconnected = false;
this.packetId = 1;
this.keepaliveTimer = null;
this.reconnectTimer = null;
this.eventListeners = {};
this.connectTimeoutTimer = null;
this.reconnectAttempts = 0; // 重连计数
// 重连逻辑
this.reconnect = () => {
if (this.disconnected || this.reconnectTimer) return;
this.reconnectTimer = setTimeout(() => {
this.connect();
this.reconnectTimer = null;
}, this.options.reconnectPeriod);
};
// 心跳逻辑
this.startKeepalive = () => {
if (this.keepaliveTimer) clearInterval(this.keepaliveTimer);
this.keepaliveTimer = setInterval(() => {
if (this.connected && this.socket && this.socketCreated) {
var pingPacket = new Uint8Array([PacketType.PINGREQ << 4, 0]);
this.send(pingPacket);
}
}, this.options.keepalive * 1000);
};
}
// 绑定事件
MqttClient.prototype.on = function (event, callback) {
if (!this.eventListeners[event]) {
this.eventListeners[event] = [];
}
this.eventListeners[event].push(callback);
};
// 触发事件
MqttClient.prototype.emit = function (event, ...args) {
var listeners = this.eventListeners[event] || [];
listeners.forEach(callback => {
try {
callback(...args);
} catch (e) {
console.error('MQTT事件回调错误:', e);
}
});
};
// 发送数据(跨端适配)
MqttClient.prototype.send = function (data) {
if (!this.connected || !this.socket || !this.socketCreated) return;
try {
// 转换为ArrayBuffer(小程序真机更兼容)
const arrayBuffer = data.buffer.slice(
data.byteOffset,
data.byteOffset + data.byteLength
);
if (isMiniProgram) {
this.socket.send({ data: arrayBuffer }); // 用ArrayBuffer发送
} else if (isH5) {
this.socket.send(arrayBuffer);
}
} catch (e) {
console.error('MQTT发送数据失败:', e);
this.emit('error', e);
}
};
// 安全关闭Socket(跨端适配)
MqttClient.prototype.safeCloseSocket = function () {
if (this.socket && this.socketCreated) {
try {
if (isMiniProgram) {
this.socket.close({ code: 1000, reason: 'normal closure' });
} else if (isH5) {
this.socket.close(1000, 'normal closure');
}
} catch (e) {
console.warn('关闭Socket失败(非关键):', e);
}
}
// 重置状态
this.socket = null;
this.socketCreated = false;
this.connected = false;
};
// 连接MQTT服务器(核心:跨端API适配,固定地址)
MqttClient.prototype.connect = function () {
// 安全关闭旧连接
this.safeCloseSocket();
// 清空定时器
if (this.connectTimeoutTimer) clearTimeout(this.connectTimeoutTimer);
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
this.connected = false;
// 固定连接地址:wss://mq.dtszbj.com/mqtt
var url = 'wss://mq.dtszbj.com/mqtt';
console.log(`MQTT连接(${isMiniProgram ? '小程序' : 'H5'}):`, url);
try {
// ========== 小程序Socket创建 ==========
if (isMiniProgram) {
this.socket = wx.connectSocket({
url: url,
header: { 'content-type': 'application/json' },
protocols: ['mqtt']
});
this.socketCreated = true;
// 小程序事件绑定(修复onOpen调用)
this.socket.onOpen((res) => {
console.log('MQTT Socket(小程序)连接打开:', res);
try {
var connectPacket = createConnectPacket(this.options);
this.socket.send({ data: connectPacket });
} catch (e) {
console.error('发送CONNECT报文失败:', e);
this.emit('error', e);
this.safeCloseSocket();
return;
}
// 连接超时处理
this.connectTimeoutTimer = setTimeout(() => {
if (!this.connected) {
const timeoutErr = new Error(`Connection timeout (${this.options.timeout}ms)`);
console.error(`MQTT连接超时: 服务器未在${this.options.timeout}ms内响应`, timeoutErr);
this.emit('error', timeoutErr);
// 更安全的关闭方式
this.safeCloseSocket();
// 指数退避重连策略
const currentReconnectPeriod = this.options.reconnectPeriod * Math.min(
Math.pow(2, this.reconnectAttempts || 0),
30000 // 最大重连间隔30秒
);
this.reconnectAttempts = (this.reconnectAttempts || 0) + 1;
console.log(`将在${currentReconnectPeriod}ms后尝试重连...`);
setTimeout(() => this.reconnect(), currentReconnectPeriod);
}
}, this.options.timeout);
});
this.socket.onMessage((res) => {
try {
var buffer = new Uint8Array(res.data);
this.handlePacket(buffer);
} catch (e) {
console.error('解析MQTT报文失败:', e);
this.emit('error', e);
}
});
this.socket.onError((err) => {
console.error('MQTT Socket(小程序)错误:', err);
this.emit('error', err);
this.safeCloseSocket();
this.reconnect();
});
this.socket.onClose((res) => {
console.log('MQTT Socket(小程序)关闭:', res);
this.socketCreated = false;
this.connected = false;
this.emit('close', res);
if (this.keepaliveTimer) clearInterval(this.keepaliveTimer);
if (this.connectTimeoutTimer) clearTimeout(this.connectTimeoutTimer);
if (!this.disconnected) {
this.reconnect();
}
});
// ========== H5 WebSocket创建 ==========
} else if (isH5) {
this.socket = new WebSocket(url, ['mqtt']);
this.socket.binaryType = 'arraybuffer';
this.socketCreated = true;
// H5事件绑定(替换小程序的onOpen为onopen)
this.socket.onopen = (res) => {
console.log('MQTT Socket(H5)连接打开:', res);
try {
var connectPacket = createConnectPacket(this.options);
this.socket.send(connectPacket);
} catch (e) {
console.error('发送CONNECT报文失败:', e);
this.emit('error', e);
this.safeCloseSocket();
return;
}
// 连接超时处理
this.connectTimeoutTimer = setTimeout(() => {
if (!this.connected) {
var timeoutErr = new Error('Connection timeout');
this.emit('error', timeoutErr);
this.safeCloseSocket();
this.reconnect();
}
}, this.options.timeout);
};
this.socket.onmessage = (res) => {
try {
var buffer = new Uint8Array(res.data);
this.handlePacket(buffer);
} catch (e) {
console.error('解析MQTT报文失败:', e);
this.emit('error', e);
}
};
this.socket.onerror = (err) => {
console.error('MQTT Socket(H5)错误:', err);
this.emit('error', err);
this.safeCloseSocket();
this.reconnect();
};
this.socket.onclose = (res) => {
console.log('MQTT Socket(H5)关闭:', res);
this.socketCreated = false;
this.connected = false;
this.emit('close', res);
if (this.keepaliveTimer) clearInterval(this.keepaliveTimer);
if (this.connectTimeoutTimer) clearTimeout(this.connectTimeoutTimer);
if (!this.disconnected) {
this.reconnect();
}
};
} else {
throw new Error('不支持当前运行环境');
}
} catch (e) {
console.error('创建Socket失败:', e);
this.socketCreated = false;
this.emit('error', new Error('创建Socket失败:' + e.message));
this.reconnect();
return;
}
};
// 处理收到的报文(无TextDecoder依赖,支持中文)
MqttClient.prototype.handlePacket = function (buffer) {
try {
var offset = 0;
var type = (buffer[offset] >> 4) & 0x0F;
offset++;
var remaining = decodeRemainingLength(buffer, offset);
offset = remaining.offset;
switch (type) {
case PacketType.CONNACK:
if (this.connectTimeoutTimer) clearTimeout(this.connectTimeoutTimer);
var connAckCode = buffer[offset + 1];
if (connAckCode === 0) {
this.connected = true;
this.emit('connect');
this.startKeepalive();
} else {
var errors = [
'Connection Accepted',
'Unacceptable Protocol Version',
'Identifier Rejected',
'Server Unavailable',
'Bad User Name or Password',
'Not Authorized'
];
var connErr = new Error(errors[connAckCode] || 'Unknown error');
this.emit('error', connErr);
this.safeCloseSocket();
this.reconnect();
}
break;
case PacketType.SUBACK:
this.emit('suback', buffer);
break;
case PacketType.PUBLISH:
var topicLength = (buffer[offset] << 8) | buffer[offset + 1];
offset += 2;
// 手动解码主题(支持中文)
var topic = uint8ArrayToString(buffer.slice(offset, offset + topicLength));
offset += topicLength;
var payload = buffer.slice(offset, offset + remaining.length - (topicLength + 2));
// 手动解码消息内容(支持中文)
var message = uint8ArrayToString(payload, this.options.encoding);
this.emit('message', topic, message);
break;
case PacketType.PINGRESP:
break;
default:
break;
}
} catch (e) {
console.error('处理MQTT报文异常:', e);
this.emit('error', e);
}
};
// 订阅主题
MqttClient.prototype.subscribe = function (topic, callback) {
callback = callback || function () { };
if (!this.connected || !this.socketCreated) {
var err = new Error('Not connected');
this.emit('error', err);
callback(err);
return;
}
try {
var packetId = this.packetId++;
var subscribePacket = createSubscribePacket(topic, packetId);
this.send(subscribePacket);
var subackHandler = () => {
this.eventListeners.suback = this.eventListeners.suback.filter(fn => fn !== subackHandler);
callback(null);
};
this.on('suback', subackHandler);
} catch (e) {
console.error('MQTT订阅主题失败:', e);
this.emit('error', e);
callback(e);
}
};
// 发布消息(保留有效实现,删除重复的空实现)
MqttClient.prototype.publish = function (topic, message) {
if (!this.connected || !this.socketCreated) {
var err = new Error('Not connected');
this.emit('error', err);
return;
}
try {
// 编码主题和消息
var topicBuf = stringToUint8Array(topic);
var topicLenBuf = new Uint8Array([(topicBuf.length >> 8) & 0xFF, topicBuf.length & 0xFF]);
var messageBuf = stringToUint8Array(message);
// 固定头(QoS 0,不保留)
var fixedHeader = new Uint8Array([PacketType.PUBLISH << 4]);
// 剩余长度 = 主题长度(2字节) + 主题内容 + 消息内容
var remainingLength = 2 + topicBuf.length + messageBuf.length;
var remainingLengthBuf = encodeRemainingLength(remainingLength);
// 拼接报文
var packetParts = [
fixedHeader,
remainingLengthBuf,
topicLenBuf,
topicBuf,
messageBuf
];
var totalLength = 0;
packetParts.forEach(part => totalLength += part.length);
var packet = new Uint8Array(totalLength);
var offset = 0;
packetParts.forEach(part => {
packet.set(part, offset);
offset += part.length;
});
this.send(packet);
} catch (e) {
console.error('发送消息失败:', e);
this.emit('error', e);
}
};
// 取消订阅
MqttClient.prototype.unsubscribe = function (topic) {
this.emit('warn', 'unsubscribe方法未实现');
};
// 关闭连接
MqttClient.prototype.end = function (force) {
this.disconnected = true;
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
if (this.keepaliveTimer) clearInterval(this.keepaliveTimer);
if (this.connectTimeoutTimer) clearTimeout(this.connectTimeoutTimer);
this.safeCloseSocket();
this.emit('close');
};
// 导出connect方法
var mqtt = {
connect: function (options, urlOptions) {
var finalOptions = typeof options === 'string' ? parseUrl(options) : options;
var client = new MqttClient(finalOptions);
client.connect();
return client;
}
};
return mqtt;
})));
二、核心优势
-
双端无缝兼容:同时支持微信小程序(wx API)和 H5(window WebSocket),一套代码两处运行
-
彻底解决兼容性报错:
- 修复
onOpen 不是函数、task not found经典报错 - 摒弃
TextEncoder/TextDecoder依赖,手动实现 UTF-8 编解码,适配所有小程序基础库版本
- 修复
-
中文友好:完美解决 MQTT 消息中文乱码问题,收发中文无压力
-
稳定可靠:内置自动重连(指数退避策略)、心跳保活、连接超时处理,杜绝意外断连
-
开箱即用:已写死核心配置
wss://mq.dtszbj.com/mqtt,无需手动配置 URL、协议等参数 -
轻量无依赖:无第三方 npm 包依赖,直接引入即可使用,体积小巧
三、快速使用步骤
1. 保存文件
将上方完整 JS 代码复制,保存为 mqtt-wx.js 文件,放入你的小程序 / H5 项目目录(如 utils/mqtt-wx.js)
2. 引入文件
微信小程序中引入
// 在需要使用的页面/组件js文件中
const mqtt = require('../../utils/mqtt-wx.js');
H5 中引入
<!-- 方式1:script标签引入(直接使用) -->
<script src="./utils/mqtt-wx.js"></script>
<!-- 方式2:ES6模块化引入(需打包工具支持,如Webpack/Vite) -->
import mqtt from './utils/mqtt-wx.js';
3. 核心使用示例(可直接复制运行)
// 1. 创建MQTT客户端(无需传参,已固定对接wss://mq.dtszbj.com/mqtt)
const mqttClient = mqtt.connect();
// 2. 监听连接成功事件
mqttClient.on('connect', () => {
console.log('✅ MQTT连接成功!');
// 3. 订阅指定主题(示例:订阅test_topic主题)
mqttClient.subscribe('test_topic', (err) => {
if (!err) {
console.log('✅ 订阅主题test_topic成功!');
// 4. 发布中文消息(向test_topic主题推送消息)
mqttClient.publish('test_topic', '大家好!这是一条兼容小程序和H5的MQTT中文消息');
} else {
console.error('❌ 订阅主题失败:', err);
}
});
});
// 5. 监听收到的消息(核心:接收订阅主题的推送内容)
mqttClient.on('message', (topic, msg) => {
console.log(`📩 收到主题【${topic}】的消息:`, msg);
// 在这里编写消息处理逻辑(如渲染到页面、触发业务操作等)
});
// 6. 监听错误事件(排查问题必备)
mqttClient.on('error', (err) => {
console.error('❌ MQTT异常:', err);
});
// 7. 监听连接关闭事件
mqttClient.on('close', () => {
console.log('🔴 MQTT连接已关闭');
});
// 8. 手动关闭MQTT连接(如需退出页面/销毁组件时调用)
// mqttClient.end();
四、关键功能说明
1. 自动环境检测
代码内部自动判断运行环境,无需手动切换配置:
var isMiniProgram = typeof wx !== 'undefined' && typeof wx.connectSocket === 'function';
var isH5 = !isMiniProgram && typeof window !== 'undefined' && typeof window.WebSocket === 'function';
2. 手动 UTF-8 编解码(解决中文乱码核心)
stringToUint8Array(str):将字符串(含中文)转为 MQTT 协议支持的 UTF-8 Uint8Array 格式uint8ArrayToString(buf):将收到的二进制数据解码为 UTF-8 字符串,完美支持中文
3. 稳定重连与心跳保活
- 自动重连:连接断开后自动触发重连,采用指数退避策略(最大间隔 30 秒),避免频繁请求服务器
- 心跳保活:默认 60 秒发送一次 PING 报文,维持长连接,防止被服务器主动断开
4. 核心 API 列表
| API 方法 | 功能说明 |
|---|---|
mqtt.connect() | 创建并初始化 MQTT 客户端,自动发起连接 |
client.on(event, cb) | 绑定事件监听(connect/message/error/close) |
client.subscribe(topic, cb) | 订阅指定 MQTT 主题 |
client.publish(topic, msg) | 向指定主题发布消息 |
client.end() | 手动关闭 MQTT 连接,停止自动重连 |
五、常见问题排查
-
小程序提示 “未配置合法域名”
- 解决方案:小程序后台 → 开发 → 开发设置 → 服务器域名,添加
mq.dtszbj.com到「socket 合法域名」和「request 合法域名」 - 开发环境临时方案:微信开发者工具 → 详情 → 本地设置 → 勾选 “不校验合法域名、HTTPS 证书”
- 解决方案:小程序后台 → 开发 → 开发设置 → 服务器域名,添加
-
连接超时 / 无法连接
- 检查网络是否正常,
mq.dtszbj.com服务是否可用 - 小程序端确认已配置合法域名,H5 端确认无跨域限制(服务端需开启 CORS)
- 确认设备能正常访问
wss://mq.dtszbj.com/mqtt
- 检查网络是否正常,
-
消息收发异常
- 确保
connect事件触发后,再执行订阅 / 发布操作 - 检查主题名称是否一致(MQTT 主题区分大小写)
- 通过
error事件监听错误信息,辅助排查问题
- 确保
总结
这款 mqtt-wx.js 终极兼容版,专为小程序和 H5 场景量身打造,彻底解决了常规 MQTT 库的兼容性痛点,中文支持友好,无需复杂配置,直接引入即可快速实现 MQTT 消息收发功能,极大提升开发效率,节省排错时间!