- 前端
【免费下载链接】vueuse
Collection of essential Vue Composition Utilities for Vue 3
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 反馈写法,可供参考。
返回值一览
| 属性 | 类型 | 说明 |
|---|---|---|
isSupported | ComputedRef<boolean> | 当前环境是否支持 Web Bluetooth API |
isConnected | Readonly<ShallowRef<boolean>> | 设备当前是否已连接 |
device | ShallowRef<BluetoothDevice \| undefined> | 已连接的蓝牙设备对象 |
server | ShallowRef<BluetoothRemoteGATTServer \| undefined> | 已连接设备的 GATT 服务端对象 |
error | ShallowRef<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>这段代码有三个值得注意的技术细节:
optionalServices必不可少:BLE 设备只会暴露其广播中声明的服务。battery_service这类标准服务必须在optionalServices中显式声明,requestDevice才会把它们暴露给网页,否则getPrimaryService('battery_service')会失败。- 事件监听代替轮询:
useEventListener把characteristicvaluechanged事件绑定到特征值对象上,事件回调里event.target.value.getUint8(0)负责把 DataView 缓冲区中的第一个字节解析成 0–100 的电量百分比;{ passive: true }声明该监听不会调用preventDefault(),可获得更好的性能表现。 - 一次性 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):
- 能力检测:
useSupported(() => navigator && 'bluetooth' in navigator)计算isSupported。requestDevice内部也会再次检查,在不支持的环境下直接返回,避免报错。 - 请求设备:
requestDevice()调用navigator.bluetooth.requestDevice({ acceptAllDevices, filters, optionalServices }),把device写入浅层 ref,并把任何异常捕获到error中。 - 自动连接:通过
watch(device, ...)监听设备变化,一旦拿到设备就调用connectToBluetoothGATTServer():校验device.gatt存在后执行device.gatt.connect(),得到BluetoothRemoteGATTServer存入server,并用server.connected同步isConnected。组件挂载时(tryOnMounted)也会主动尝试连接一次。 - 断开与清理:挂载时注册
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
相关推荐
AIRI 项目实战:用 VueUse useBluetooth 在 Vue 3 中接入 Web Bluetooth 低功耗设备
AIRI 项目实战:用 VueUse useBluetooth 在 Vue 3 中接入 Web Bluetooth 低功耗设备 导读 useBluetooth
AI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染VueUse `useBluetooth` 深度指南:基于 Web Bluetooth API 的响应式 BLE 设备连接
VueUse useBluetooth 深度指南:基于 Web Bluetooth API 的响应式 BLE 设备连接 useBluetooth 是 VueUs
前端在 Vue 3 应用中用 VueUse useDeviceOrientation 响应式接入设备方向传感器
在 Vue 3 应用中用 VueUse useDeviceOrientation 响应式接入设备方向传感器 导读 useDeviceOrientation 是
AI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考