vue3+pinia+mqtt实时响应连接

一、前期准备

1. 安装mqtt

npm install mqtt

2. 引入pinia,mqtt,ref响应式,配置mqtt连接参数
javascript 复制代码
import { defineStore } from 'pinia' //引入pinia
import mqtt from 'mqtt' //引入mqtt
import { ref } from 'vue'
const mqttUrl = 'ws://broker.emqx.io:8083/mqtt' //连接地址
const options = { //配置连接参数
    clean: true, //每次连接都是新对话
    connectTimeout: 4000, //发起连接后,4秒没连上就失败
}
3. 创建变量

创建pinia实例,创建mqtt实例,添加mqtt是否连接标识,mqtt返回的消息体,需要订阅的列表

javascript 复制代码
export const useMqttStore = defineStore('mqtt', () => {
    const client = ref(null) //mqtt实例
    const isConnected = ref(false) //mqtt连接标识
    const messages = ref([]) //mqtt返回的消息列表
    const subscribeTopics = ref([]) //订阅主题列表

二、连接mqtt

第 1 步:创建客户端 + 发起连接(真正连接的动作)

相当于拨打电话!!

javascript 复制代码
 // 连接 MQTT
    const connect = () => {
        //1.创建客户端
        client.value = mqtt.connect(mqttUrl, options)
第 2 步:注册"连接成功"监听

client.value.on('connect' 这句的意思的,我只是注册了一个监听,监听connect事件。并不是主动连接成功,是被动的!!

连接需要时间,不是立刻连上的。 JS 不会卡住等它,而是继续跑后面代码。 (异步)

javascript 复制代码
  // 2.连接成功
        client.value.on('connect', () => {
            console.log('✅ MQTT 连接成功')
            isConnected.value = true
            subscribe()
        })
第 3 步:订阅主题

如果监听到连接成功,那么一定要订阅主题

client.value.subscribe 这个订阅方法是主动的

MQTT 靠主题分发消息,订阅主题 = 告诉服务器:我只接收这个主题下的数据。

javascript 复制代码
    // 2. 订阅主题
    const subscribe = () => {
        if (!client.value || !isConnected.value) return
        subscribeTopics.value.forEach((topic) => {
            client.value.subscribe(topic, (err) => {
                if (!err) console.log(`📥 已订阅:${topic}`)
            })
        })
    }
第 4 步:注册"接收消息"监听

监听message事件:它的作用是: 告诉程序:一旦有消息来了,就按这个方法处理。

参数: topic 主题名称 、 message 消息内容

javascript 复制代码
      // 3.接收消息
        client.value.on('message', (topic, message) => {
            const msg = message.toString()
            console.log('📩 收到消息:', topic, msg)
            messages.value.push({ topic, message: msg })
        })
第 5 步:注册"断开连接"相关的监听
1. 监听异常断开(断网、服务器挂了、超时)
javascript 复制代码
    client.value.on('close', () => {
            console.log('❌ MQTT 连接断开(异常/掉线)')
            isConnected.value = false // UI 必更新
        })
2. 监听主动断开 你主动调用 client.value.end()(正常退出)
javascript 复制代码
     // 2.主动断开
        client.value.on('disconnect', () => {
            console.log('❌ MQTT 主动断开')
            isConnected.value = false
        })
**3. 监听被动断开 (**网络断了、WiFi 掉了、服务器崩溃 / 重启 等)
javascript 复制代码
        // 被动断开
        client.value.on('offline', () => {
            console.log('📴 MQTT离线');
            isConnected.value = false;
        });
4. 监听报错 (MQTT 服务器地址写错、端口号写错、用户名 / 密码错误、客户端 ID 重复 等)

一句话: 连都连不上,直接报错 → 触发 error

javascript 复制代码
       // 3.错误
        client.value.on('error', (err) => {
            console.error('❌ MQTT 错误:', err)
            isConnected.value = false
        })
5. 监听重连 (重连中)

当客户端检测到连接异常断开 ,MQTT.js 内置自动重连机制开始尝试重连时,每一次发起重连请求都会触发

javascript 复制代码
  // 2. 正在重连(核心监听)
    client.value.on('reconnect', () => {
      console.log('🔄 MQTT 重连中...')
      })

三、其他部分

1. 主动给mqtt发布消息

topic

你要发到哪个主题(频道) 比如:"device/light/switch"

payload

你要发的内容(消息体) 比如:"on""off"{ temperature: 25 }

javascript 复制代码
    // 3. 发布消息
    const publishMsg = (topic, payload) => {
        if (!isConnected.value) {
            alert('未连接 MQTT!')
            return
        }
        client.value.publish(topic, payload, (err) => {
            if (!err) console.log(`📤 发布成功:${topic}`)
        })
    }
2. 主动断开mqtt

client.value.end() 断开mqtt

javascript 复制代码
    // 4. 主动断开
    const disconnectMqtt = () => {
        if (client.value) {
            client.value.end()
            client.value = null
            isConnected.value = false
            console.log('✅ 手动断开 MQTT')
        }
    }

四、整体代码

javascript 复制代码
import { defineStore } from 'pinia'
import mqtt from 'mqtt'
import { ref, onUnmounted } from 'vue'

// 公共 MQTT 服务器
const mqttUrl = 'ws://broker.emqx.io:8083/mqtt'

// 生成唯一 clientId(防止多人互踢)
const clientId = `mqtt_${Math.random().toString(16).slice(3)}`

const options = {
  clean: true,
  connectTimeout: 4000,
  clientId, // 必须加!
}

export const useMqttStore = defineStore('mqtt', () => {
  const client = ref(null)
  const isConnected = ref(false)
  const messages = ref([])
  const subscribeTopics = ref([]) // 要订阅的主题数组

  // =========================================
  // 连接 MQTT(防重复连接)
  // =========================================
  const connect = () => {
    // 已经连接 / 正在连接 → 直接返回
    if (client.value || isConnected.value) return

    // 创建客户端
    client.value = mqtt.connect(mqttUrl, options)

    // 连接成功
    client.value.on('connect', () => {
      console.log('✅ MQTT 连接成功')
      isConnected.value = true
      reSubscribe() // 重连后必须重新订阅!
    })

    // 收到消息
    client.value.on('message', (topic, message) => {
      const msg = message.toString()
      console.log('📩 收到消息:', topic, msg)
      messages.value.push({ topic, message: msg })
    })

    // 正在重连
    client.value.on('reconnect', () => {
      console.log('🔄 MQTT 重连中...')
      isConnected.value = false // 重连中 = 未连接
    })

    // 连接断开
    client.value.on('close', () => {
      console.log('❌ MQTT 连接断开')
      isConnected.value = false
    })

    // 离线
    client.value.on('offline', () => {
      console.log('📴 MQTT 离线')
      isConnected.value = false
    })

    // 错误
    client.value.on('error', (err) => {
      console.error('❌ MQTT 错误:', err)
      isConnected.value = false
    })
  }

  // =========================================
  // 订阅所有主题(重连时自动调用)
  // =========================================
  const reSubscribe = () => {
    if (!client.value || !isConnected.value) return
    const topics = subscribeTopics.value

    if (topics.length === 0) {
      console.log('⚠️ 暂无订阅主题')
      return
    }

    topics.forEach((topic) => {
      client.value?.subscribe(topic, (err) => {
        if (!err) console.log(`📥 已订阅:${topic}`)
      })
    })
  }

  // =========================================
  // 发布消息
  // =========================================
  const publishMsg = (topic, payload) => {
    if (!isConnected.value || !client.value) {
      alert('❌ MQTT 未连接')
      return
    }
    client.value.publish(topic, payload, (err) => {
      if (!err) console.log(`📤 发布成功:${topic}`)
    })
  }

  // =========================================
  // 主动断开(安全销毁)
  // =========================================
  const disconnectMqtt = () => {
    if (client.value) {
      client.value.end()
      client.value = null
      isConnected.value = false
      messages.value = []
      console.log('✅ 已手动断开 MQTT')
    }
  }

  // =========================================
  // 外部设置订阅主题数组
  // =========================================
  const subscribeArr = (arr) => {
    subscribeTopics.value = arr
  }

  // =========================================
  // 页面销毁时自动断开(重要!防内存泄漏)
  // =========================================
  onUnmounted(() => {
    disconnectMqtt()
  })

  return {
    client,
    isConnected,
    messages,
    connect,
    subscribeArr,
    publishMsg,
    disconnectMqtt,
  }
})

四、总结

1. 安装

npm install mqtt

2. 基础连接

创建mqtt实例: let client = null

连接mqtt:client = mqtt.connect('ws://ip:port/mqtt')

监听连接成功:client.on('connect', () => {})

监听消息返回:client.on('message', (topic, msg) => {})

监听mqtt重连:client.on('reconnect', () => {})

监听mqtt错误:client.on('error', () => {})

监听mqtt断开:client.on('close', () => {})

javascript 复制代码
import mqtt from 'mqtt'
let client = null

// 连接Broker
const connectMqtt = () => {
  client = mqtt.connect('ws://ip:port/mqtt')
  // 连接成功
  client.on('connect', () => {})
  // 接收消息
  client.on('message', (topic, msg) => {})
  // 重连、错误、断开
  client.on('reconnect', () => {})
  client.on('error', () => {})
  client.on('close', () => {})
}
3. 订阅主题
javascript 复制代码
client.subscribe('dev/data', { qos: 1 })
4. 发布消息
javascript 复制代码
client.publish('dev/cmd', JSON.stringify(data), { qos: 1 })
5. 断开连接
javascript 复制代码
client.end()
6. 常用配置
  • qos:0/1/2 消息等级
  • keepalive:心跳保活
  • clean:是否清空历史会话
相关推荐
ayqy贾杰10 小时前
我同事,40了,他vibe coding了个App
前端·ios·客户端
i220818 Faiz Ul10 小时前
理财系统|基于java+vue的家庭理财系统小程序(源码+数据库+文档)
java·vue.js·spring boot·小程序·论文·毕设·理财系统
暗冰ཏོ10 小时前
《2026 Vue2 + Vue3 完整学习指南:基础语法、路由缓存、登录拦截、项目实战与面试题》
前端·vue.js·vue·vue3·vue2
蜡台10 小时前
VUE 侧边按钮组,可自定义位置
前端·javascript·css
AI科技星10 小时前
维度原本——基于超复数谱系的全域维度统一理论
c语言·前端·javascript·网络·electron
遇事不決洛必達10 小时前
【爬虫随笔】常见加密算法特征总结
javascript·爬虫·逆向·加密算法
kyriewen10 小时前
14MB VS 15KB:前React核心成员用AI写了个排版库,让Safari快了一千倍
前端·javascript·react.js
幸运小圣11 小时前
动态表格在 Vue 3 中的实现指南【前端】
前端·javascript·vue.js
SwJieJie11 小时前
Day 3|表格表单分页范式与 vue-request 最佳实践:从配置驱动到业务落地
前端·javascript·vue.js