☰
VueUse useBluetooth 全解析:在 Vue 3 中响应式接入 Web Bluetooth 低功耗设备
2026/10/5 1:48:43 网站建设 项目流程
  • 前端

【免费下载链接】vueuse

Collection of essential Vue Composition Utilities for Vue 3

项目地址:https://gitcode.com/gh_mirrors/vu/vueuse
点击查看免费下载

useBluetooth是 VueUse 为 Vue 3 提供的一个组合式函数,它把浏览器原生的 Web Bluetooth API 封装成响应式状态,让你能以声明式的方式发现、配对并连接蓝牙低功耗(Bluetooth Low Energy,BLE)外设。阅读完本文,你将掌握useBluetooth的完整配置项、返回值语义、如何读取特征值(如电池电量)以及如何监听设备断开与数据变化,并能直接落地到真实项目中。

什么是 Web Bluetooth,以及它适合做什么

Web Bluetooth API 允许网站通过蓝牙 4 无线标准,利用 Generic Attribute Profile(GATT)协议发现并与周边设备通信。这意味着浏览器页面可以直接读取心率计、温度传感器、智能手环等 BLE 外设的数据,无需安装任何本地驱动或原生应用。

useBluetooth所做的正是把这一套底层 API 转化为 Vue 的响应式数据流,核心能力包括:

  • 检测当前环境是否支持 Web Bluetooth API;
  • 弹出系统选择器请求用户授权并选择设备;
  • 自动连接设备的 GATT 服务端;
  • 追踪连接状态、设备对象、服务端对象与错误信息;
  • 在组件卸载时自动断开连接并清理监听。

基本用法:请求并连接一台设备

在<script setup>中引入useBluetooth,并传入acceptAllDevices: true即可发起最简单的连接流程:

<script setup lang="ts"> import { useBluetooth } from '@vueuse/core' const { isSupported, isConnected, device, requestDevice, server, error, } = useBluetooth({ acceptAllDevices: true, }) </script> <template> <button @click="requestDevice()"> Request Bluetooth Device </button> <div v-if="error"> Error: {{ error }} </div> </template>

点击按钮后,浏览器会弹出设备选择器;用户选定设备并授权后,useBluetooth内部会自动完成 GATT 连接,随后server与isConnected会响应式地更新。仓库中的 demo.vue 还额外展示了isSupported、device.name与连接状态的 UI 反馈写法,可供参考。

返回值一览

属性类型说明
isSupportedComputedRef<boolean>当前环境是否支持 Web Bluetooth API
isConnectedReadonly<ShallowRef<boolean>>设备当前是否已连接
deviceShallowRef<BluetoothDevice \| undefined>已连接的蓝牙设备对象
serverShallowRef<BluetoothRemoteGATTServer \| undefined>已连接设备的 GATT 服务端对象
errorShallowRef<unknown \| null>请求或连接过程中产生的错误
requestDevice() => Promise<void>触发设备请求的函数

当设备完成配对并连接成功后,你就可以基于server对象自由地进行服务发现与特征值读写(见下文电池电量示例)。

需要说明的是,文档中这些返回值在源码 packages/core/useBluetooth/index.ts 里实际使用的是浅层响应式ShallowRef,且isConnected通过shallowReadonly对外暴露为只读,避免外部误改内部状态。isSupported则来自useSupported(见 packages/core/useSupported/index.ts),它会在组件挂载后计算navigator.bluetooth是否存在。

进阶实战:读取并订阅电池电量变化

这个示例演示了如何用 Web Bluetooth 读取附近 BLE 设备广播的电池电量,并监听后续的电量变化通知。核心思路是:拿到battery_service服务,再取得battery_level特征值,通过characteristicvaluechanged事件持续接收最新电量。

<script setup lang="ts"> import { useBluetooth, useEventListener, watchPausable } from '@vueuse/core' const { isSupported, isConnected, device, requestDevice, server, } = useBluetooth({ acceptAllDevices: true, optionalServices: [ 'battery_service', ], }) const batteryPercent = ref<undefined | number>() const isGettingBatteryLevels = ref(false) async function getBatteryLevels() { isGettingBatteryLevels.value = true // 获取电池服务: const batteryService = await server.getPrimaryService('battery_service') // 获取当前电量特征值: const batteryLevelCharacteristic = await batteryService.getCharacteristic( 'battery_level', ) // 监听特征值变化事件 `characteristicvaluechanged`: useEventListener(batteryLevelCharacteristic, 'characteristicvaluechanged', (event) => { batteryPercent.value = event.target.value.getUint8(0) }, { passive: true }) // 主动读取一次当前电量: const batteryLevel = await batteryLevelCharacteristic.readValue() batteryPercent.value = await batteryLevel.getUint8(0) } const { stop } = watchPausable(isConnected, (newIsConnected) => { if (!newIsConnected || !server.value || isGettingBatteryLevels.value) return // 首次连接成功后读取电量: getBatteryLevels() // 后续变化由事件监听处理,因此停止该 watcher: stop() }) </script> <template> <button @click="requestDevice()"> Request Bluetooth Device </button> </template>

这段代码有三个值得注意的技术细节:

  1. optionalServices必不可少:BLE 设备只会暴露其广播中声明的服务。battery_service这类标准服务必须在optionalServices中显式声明,requestDevice才会把它们暴露给网页,否则getPrimaryService('battery_service')会失败。
  2. 事件监听代替轮询:useEventListener把characteristicvaluechanged事件绑定到特征值对象上,事件回调里event.target.value.getUint8(0)负责把 DataView 缓冲区中的第一个字节解析成 0–100 的电量百分比;{ passive: true }声明该监听不会调用preventDefault(),可获得更好的性能表现。
  3. 一次性 watcher 的设计:watchPausable监听isConnected,只在首次连接成功时执行一次电量读取,随即stop()停止监听,后续电量变化全部交给事件回调处理,避免重复初始化。需要提醒的是,从源码看watchPausable当前已被标记为@deprecated(见 packages/shared/watchPausable/index.ts),官方建议改用 Vue 内建的watch,因此你在新代码中也可以直接写watch(isConnected, ...)并配合一个let once标志达到同样效果。

配置项详解:从过滤到精细匹配

useBluetooth接受一个可选的UseBluetoothOptions配置对象,其完整类型声明与源码中的定义一一对应(packages/core/useBluetooth/index.ts):

export interface UseBluetoothRequestDeviceOptions { /** * 一组蓝牙扫描过滤器。每个过滤器由服务 UUID 数组、 * name(精确设备名)与 namePrefix(设备名前缀)组成。 */ filters?: BluetoothLEScanFilter[] | undefined /** * 一组蓝牙服务 UUID,用于声明需要访问的 GATT 服务。 */ optionalServices?: BluetoothServiceUUID[] | undefined } export interface UseBluetoothOptions extends UseBluetoothRequestDeviceOptions, ConfigurableNavigator { /** * 是否允许脚本接受所有蓝牙设备,默认 false。 * * !! 开启后选择器可能列出大量无关设备, * 且由于没有过滤条件会浪费搜索功耗,请谨慎使用。 */ acceptAllDevices?: boolean }

各配置项的行为可以这样理解:

  • acceptAllDevices(默认false):允许页面接受任意蓝牙设备。它会在设备选择器中展示所有可发现的外设,因此文档明确警告这可能导致无关设备扎堆、搜索功耗浪费。源码中的处理逻辑是:如果同时传入了非空的filters,acceptAllDevices会被强制置回false(packages/core/useBluetooth/index.ts),因为两者语义冲突。
  • filters:BluetoothLEScanFilter[]扫描过滤器数组。每个过滤器可以包含services(服务 UUID)、name(精确设备名)、namePrefix(设备名前缀)等字段,用于精准缩小设备选择范围。从源码看,只要filters非空,最终传给navigator.bluetooth.requestDevice()的一定是acceptAllDevices: false。
  • optionalServices:BluetoothServiceUUID[]服务 UUID 数组。用于声明页面希望访问的额外 GATT 服务,标准服务名(如'battery_service')或 16 位 / 128 位 UUID 均可。
  • navigator:来自ConfigurableNavigator(定义见 packages/core/_configurable.ts),允许你注入自定义navigator实例,典型场景是在 iframe 或测试环境中替换全局对象。默认取window.navigator。

底层实现:连接、断开与自动清理的生命周期

深入了解源码可以解释许多使用细节。useBluetooth的实现大致分为四个阶段(packages/core/useBluetooth/index.ts):

  1. 能力检测:useSupported(() => navigator && 'bluetooth' in navigator)计算isSupported。requestDevice内部也会再次检查,在不支持的环境下直接返回,避免报错。
  2. 请求设备:requestDevice()调用navigator.bluetooth.requestDevice({ acceptAllDevices, filters, optionalServices }),把device写入浅层 ref,并把任何异常捕获到error中。
  3. 自动连接:通过watch(device, ...)监听设备变化,一旦拿到设备就调用connectToBluetoothGATTServer():校验device.gatt存在后执行device.gatt.connect(),得到BluetoothRemoteGATTServer存入server,并用server.connected同步isConnected。组件挂载时(tryOnMounted)也会主动尝试连接一次。
  4. 断开与清理:挂载时注册gattserverdisconnected事件监听,触发后执行reset()把isConnected、device、server一并清空;tryOnScopeDispose则保证组件卸载时调用device.gatt?.disconnect()主动断开连接。

仓库中的测试 index.browser.test.ts 验证了两个关键行为:其一,多次requestDevice切换设备后,gattserverdisconnected监听器不会在旧设备上累积泄漏;其二,连接完成后server与isConnected会被正确更新,模拟设备断开后状态也能正确复位。这些测试也展示了如何通过注入自定义navigator来在无真实蓝牙硬件的环境中编写单元测试。

注意事项与适用前提

使用useBluetooth前请务必确认以下限制,它们来自文档的明确提示:

  • 浏览器支持有限:Web Bluetooth API 目前仅在部分平台上部分实现,包括 Android M、Chrome OS、macOS 与 Windows 10。生产环境务必在 UI 上先检查isSupported,再决定是否展示“请求设备”按钮。
  • 规范存在诸多坑:Web Bluetooth 规范本身在设备发现与连接方面存在多种边界情况(如设备缓存、连接超时、GATT 服务不可见等),建议通读 W3C 草稿报告中的 caveat 说明。
  • Web Worker 中不可用:该 API 不会暴露在WorkerNavigator上,因此无法在 Web Worker 环境中使用useBluetooth。
  • 必须由用户手势触发:requestDevice()需要在用户点击等用户手势上下文中调用,否则浏览器会拒绝弹窗。
  • HTTPS 前提:Web Bluetooth 是安全上下文 API,网页必须运行在 HTTPS(或 localhost)环境下才能使用。

小结

useBluetooth把复杂的 Web Bluetooth 生命周期——能力检测、设备请求、GATT 连接、断开复位、资源清理——收敛成一组简洁的响应式状态与一个触发函数,让 Vue 开发者可以像使用普通响应式变量一样操作 BLE 外设。配合useEventListener监听特征值变化,你就能以极少的样板代码构建出真实的 BLE 交互应用。继续阅读本仓库的 index.md 文档 与 源码实现,可以进一步探索更深层的 API 细节。

  • 前端

【免费下载链接】vueuse

Collection of essential Vue Composition Utilities for Vue 3

项目地址:https://gitcode.com/gh_mirrors/vu/vueuse
点击查看免费下载

相关推荐

上一篇:终极指南:如何用开源工具解决艾尔登法环存档迁移难题
下一篇:Gun.js单元测试指南:确保实时应用稳定性

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询