Deprecated: 自 6.9.0 版本起,使用参数调用函数 WP_Dependencies->add_data() 已弃用!IE conditional comments are ignored by all supported browsers. in D:\wwwroot\zyxy\wordpress\wp-includes\functions.php on line 6131

Deprecated: 自 6.9.0 版本起,使用参数调用函数 WP_Dependencies->add_data() 已弃用!IE conditional comments are ignored by all supported browsers. in D:\wwwroot\zyxy\wordpress\wp-includes\functions.php on line 6131

Vue集成Web Bluetooth/NFC API:将设备连接与状态变化纳入响应性依赖图

Vue.js 集成 Web Bluetooth/NFC API:响应式设备连接与状态管理

大家好!今天我们来深入探讨如何在 Vue.js 应用中集成 Web Bluetooth 和 Web NFC API,并巧妙地将设备连接状态和数据变化纳入 Vue 的响应式依赖图中,从而构建更流畅、更直观的用户体验。

Web Bluetooth API 概述

Web Bluetooth API 允许网页直接与用户附近的蓝牙设备进行通信。它提供了扫描设备、连接设备、读写 GATT 特性等功能,为 Web 应用带来了与物联网设备交互的可能性。

核心概念:

  • BluetoothDevice: 代表一个蓝牙设备。
  • BluetoothRemoteGATTServer: 代表设备上的 GATT 服务器。
  • BluetoothService: GATT 服务器提供的服务,例如心率监测服务。
  • BluetoothCharacteristic: 服务中的特性,代表数据的具体属性,例如心率值。
  • GATT (Generic Attribute Profile): 定义了蓝牙设备之间如何交换数据的协议。

基本流程:

  1. 请求设备: 使用 navigator.bluetooth.requestDevice() 提示用户选择要连接的蓝牙设备。
  2. 连接 GATT 服务器: 获取 BluetoothDevice 后,调用 device.gatt.connect() 连接 GATT 服务器。
  3. 获取服务和特性: 从 GATT 服务器获取所需的服务和特性。
  4. 读写特性: 使用 characteristic.readValue()characteristic.writeValue() 读写特性值。
  5. 监听特性变化: 使用 characteristic.startNotifications() 监听特性值的变化。

Web NFC API 概述

Web NFC API 允许网页读写 NFC (Near Field Communication) 标签。它可以用于读取标签中的数据,例如 URL、文本或自定义数据,也可以将数据写入标签。

核心概念:

  • NDEF (NFC Data Exchange Format): 用于在 NFC 标签上存储数据的标准格式。
  • NDEFMessage: 包含一个或多个 NDEF 记录的消息。
  • NDEFRecord: 包含特定类型的数据的记录,例如 URL、文本或 MIME 类型的数据。

基本流程:

  1. 检查支持性: 使用 NDEFReader 构造函数检查浏览器是否支持 Web NFC API。
  2. 监听扫描事件: 调用 reader.scan() 启动扫描,并监听 reading 事件,该事件在检测到 NFC 标签时触发。
  3. 读取标签数据:reading 事件处理程序中,访问 event.message 获取 NDEFMessage 对象,并从中提取 NDEFRecord
  4. 写入标签数据: 创建 NDEFMessageNDEFRecord 对象,然后调用 reader.write() 将数据写入标签。

Vue.js 集成策略:响应式连接与状态管理

在 Vue.js 中集成这两个 API 的关键在于如何将异步操作和状态变化融入 Vue 的响应式系统中。我们可以采用以下策略:

  1. 使用 ref 创建响应式状态: 使用 ref 创建响应式变量来存储设备连接状态、数据和错误信息。
  2. 使用 reactive 创建响应式对象: 使用 reactive 创建响应式对象来管理更复杂的状态,例如设备信息和服务/特性列表。
  3. 使用 computed 派生计算属性: 使用 computed 创建计算属性来根据响应式状态派生出新的状态,例如设备是否已连接。
  4. 在组合式函数中封装 API 调用: 创建组合式函数来封装 Web Bluetooth 和 Web NFC API 的调用,并将响应式状态作为返回值。
  5. 使用 watch 监听状态变化: 使用 watch 监听状态变化,并在状态变化时执行副作用,例如更新 UI 或发送数据到服务器。
  6. 错误处理和用户反馈: 完善的错误处理机制是关键。使用 try...catch 块捕获 API 调用中的错误,并使用响应式状态将错误信息显示给用户。

Web Bluetooth 集成示例

<template>
  <div>
    <button @click="connect" :disabled="isConnected">
      {{ isConnected ? '已连接' : '连接蓝牙设备' }}
    </button>
    <p v-if="deviceName">设备名称: {{ deviceName }}</p>
    <p v-if="heartRate">心率: {{ heartRate }} bpm</p>
    <p v-if="error">{{ error }}</p>
  </div>
</template>

<script setup>
import { ref, reactive, computed, onMounted, watch } from 'vue';

const isConnected = ref(false);
const deviceName = ref('');
const heartRate = ref(null);
const error = ref('');

let bluetoothDevice = null;
let heartRateCharacteristic = null;

const HEART_RATE_SERVICE_UUID = 'heart_rate';
const HEART_RATE_MEASUREMENT_CHAR_UUID = 'heart_rate_measurement';

async function connect() {
  try {
    bluetoothDevice = await navigator.bluetooth.requestDevice({
      filters: [{ services: [HEART_RATE_SERVICE_UUID] }],
      optionalServices: [HEART_RATE_SERVICE_UUID]
    });

    deviceName.value = bluetoothDevice.name;

    bluetoothDevice.addEventListener('gattserverdisconnected', onDisconnected);

    const server = await bluetoothDevice.gatt.connect();

    const service = await server.getPrimaryService(HEART_RATE_SERVICE_UUID);

    heartRateCharacteristic = await service.getCharacteristic(HEART_RATE_MEASUREMENT_CHAR_UUID);

    await heartRateCharacteristic.startNotifications();

    heartRateCharacteristic.addEventListener('characteristicvaluechanged', handleHeartRateChanged);

    isConnected.value = true;
    error.value = '';

  } catch (e) {
    error.value = e.message;
    console.error("连接失败:", e);
  }
}

function handleHeartRateChanged(event) {
  const value = event.target.value;
  // 心率格式通常是 Uint8 或 Uint16
  heartRate.value = value.getUint8(1); // 假设心率值在第二个字节
}

function onDisconnected() {
  console.log('蓝牙设备已断开连接.');
  isConnected.value = false;
  deviceName.value = '';
  heartRate.value = null;
  error.value = '';
  // 清理变量
  bluetoothDevice = null;
  heartRateCharacteristic = null;
}

onMounted(() => {
  // 检查浏览器是否支持 Web Bluetooth
  if (!navigator.bluetooth) {
    error.value = '您的浏览器不支持 Web Bluetooth API。';
  }
});

</script>

代码解释:

  • isConnected, deviceName, heartRate, error 使用 ref 创建,用于存储连接状态、设备名称、心率值和错误信息。
  • connect() 函数处理设备连接逻辑。
  • handleHeartRateChanged() 函数处理心率值变化事件,并将心率值更新到 heartRate 响应式变量。
  • onDisconnected() 函数处理设备断开连接事件,重置状态。
  • onMounted() 钩子函数检查浏览器是否支持 Web Bluetooth API。
  • HEART_RATE_SERVICE_UUIDHEART_RATE_MEASUREMENT_CHAR_UUID 是心率服务的 UUID。

关键点:

  • 连接状态 (isConnected) 和心率值 (heartRate) 都是响应式变量,当它们的值发生变化时,Vue 会自动更新 UI。
  • 错误信息 (error) 也存储在响应式变量中,方便在 UI 中显示错误信息。
  • onDisconnected 事件监听器确保在设备断开连接时,状态得到正确重置。

Web NFC 集成示例

<template>
  <div>
    <button @click="scanNFC" :disabled="isScanning">
      {{ isScanning ? '扫描中...' : '扫描 NFC 标签' }}
    </button>
    <p v-if="nfcData">NFC 数据: {{ nfcData }}</p>
    <p v-if="nfcError">{{ nfcError }}</p>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const isScanning = ref(false);
const nfcData = ref('');
const nfcError = ref('');
let ndefReader = null;

async function scanNFC() {
  isScanning.value = true;
  nfcData.value = '';
  nfcError.value = '';

  try {
    ndefReader = new NDEFReader();
    await ndefReader.scan();

    ndefReader.addEventListener('reading', ({ message }) => {
      for (const record of message.records) {
        if (record.recordType === "text") {
          const textDecoder = new TextDecoder('utf-8');
          nfcData.value = textDecoder.decode(record.data);
          break; // 找到第一个文本记录就停止
        } else if (record.recordType === "url") {
          nfcData.value = record.data; // URL可以直接显示
          break; // 找到第一个URL记录就停止
        } else {
          nfcData.value = "未知数据类型: " + record.recordType;
        }
      }
    });

    ndefReader.addEventListener('readingerror', () => {
      nfcError.value = '读取 NFC 标签时发生错误。';
      isScanning.value = false;
    });

  } catch (error) {
    nfcError.value = error.message || '发生未知错误。';
    isScanning.value = false;
  } finally {
    // 确保在完成或出错后停止扫描
    setTimeout(() => {
      isScanning.value = false; // 模拟扫描超时
      if (ndefReader) {
        ndefReader.abort();
        ndefReader = null; // 清理引用,防止内存泄漏
      }
    }, 10000); // 10秒超时
  }
}

onMounted(() => {
  if (!('NDEFReader' in window)) {
    nfcError.value = '您的浏览器不支持 Web NFC API。';
  }
});
</script>

代码解释:

  • isScanning, nfcData, nfcError 使用 ref 创建,用于存储扫描状态、NFC 数据和错误信息。
  • scanNFC() 函数处理 NFC 标签扫描逻辑。
  • reading 事件处理程序中,提取 NFC 标签中的数据,并将其更新到 nfcData 响应式变量。
  • readingerror 事件监听器处理读取错误。
  • onMounted() 钩子函数检查浏览器是否支持 Web NFC API。
  • 使用 setTimeout 模拟扫描超时,避免长时间扫描。
  • 使用 finally 块确保扫描完成后停止扫描,即使发生错误。
  • 调用 ndefReader.abort() 停止扫描,并清理 ndefReader 引用,防止内存泄漏。

关键点:

  • 扫描状态 (isScanning) 和 NFC 数据 (nfcData) 都是响应式变量,当它们的值发生变化时,Vue 会自动更新 UI。
  • 错误信息 (nfcError) 也存储在响应式变量中,方便在 UI 中显示错误信息。
  • 超时机制和 abort() 方法确保扫描操作不会无限期运行。

组合式函数封装

为了提高代码的可重用性和可维护性,可以将 Web Bluetooth 和 Web NFC API 的调用封装到组合式函数中。

Web Bluetooth 组合式函数:

// useBluetooth.js
import { ref, onUnmounted } from 'vue';

export function useBluetooth(serviceUUID, characteristicUUID) {
  const isConnected = ref(false);
  const data = ref(null);
  const error = ref('');
  let bluetoothDevice = null;
  let characteristic = null;

  async function connect() {
    try {
      bluetoothDevice = await navigator.bluetooth.requestDevice({
        filters: [{ services: [serviceUUID] }],
        optionalServices: [serviceUUID]
      });

      bluetoothDevice.addEventListener('gattserverdisconnected', onDisconnected);

      const server = await bluetoothDevice.gatt.connect();
      const service = await server.getPrimaryService(serviceUUID);
      characteristic = await service.getCharacteristic(characteristicUUID);

      await characteristic.startNotifications();
      characteristic.addEventListener('characteristicvaluechanged', handleValueChanged);

      isConnected.value = true;
      error.value = '';
    } catch (e) {
      error.value = e.message;
      console.error(e);
    }
  }

  function handleValueChanged(event) {
    data.value = event.target.value.getUint8(0); // 假设数据是 Uint8
  }

  function onDisconnected() {
    isConnected.value = false;
    data.value = null;
    error.value = '';
  }

  onUnmounted(() => {
    if (bluetoothDevice) {
      bluetoothDevice.removeEventListener('gattserverdisconnected', onDisconnected);
      bluetoothDevice = null;
    }
    if (characteristic) {
      characteristic.removeEventListener('characteristicvaluechanged', handleValueChanged);
      characteristic = null;
    }
  });

  return {
    isConnected,
    data,
    error,
    connect
  };
}

在组件中使用:

<template>
  <div>
    <button @click="bluetooth.connect" :disabled="bluetooth.isConnected">
      {{ bluetooth.isConnected ? '已连接' : '连接蓝牙设备' }}
    </button>
    <p v-if="bluetooth.data">数据: {{ bluetooth.data }}</p>
    <p v-if="bluetooth.error">{{ bluetooth.error }}</p>
  </div>
</template>

<script setup>
import { useBluetooth } from './useBluetooth';

const bluetooth = useBluetooth('heart_rate', 'heart_rate_measurement');
</script>

Web NFC 组合式函数:

// useNFC.js
import { ref, onUnmounted } from 'vue';

export function useNFC() {
  const isScanning = ref(false);
  const data = ref('');
  const error = ref('');
  let ndefReader = null;

  async function scan() {
    isScanning.value = true;
    data.value = '';
    error.value = '';

    try {
      ndefReader = new NDEFReader();
      await ndefReader.scan();

      ndefReader.addEventListener('reading', ({ message }) => {
        for (const record of message.records) {
          if (record.recordType === "text") {
            const textDecoder = new TextDecoder('utf-8');
            data.value = textDecoder.decode(record.data);
            break;
          } else if (record.recordType === "url") {
            data.value = record.data;
            break;
          } else {
            data.value = "未知数据类型: " + record.recordType;
          }
        }
      });

      ndefReader.addEventListener('readingerror', () => {
        error.value = '读取 NFC 标签时发生错误。';
        isScanning.value = false;
      });

    } catch (e) {
      error.value = e.message;
    } finally {
      setTimeout(() => {
        isScanning.value = false;
        if (ndefReader) {
          ndefReader.abort();
          ndefReader = null;
        }
      }, 10000);
    }
  }

  onUnmounted(() => {
    if (ndefReader) {
      ndefReader.abort();
      ndefReader = null;
    }
  });

  return {
    isScanning,
    data,
    error,
    scan
  };
}

在组件中使用:

<template>
  <div>
    <button @click="nfc.scan" :disabled="nfc.isScanning">
      {{ nfc.isScanning ? '扫描中...' : '扫描 NFC 标签' }}
    </button>
    <p v-if="nfc.data">NFC 数据: {{ nfc.data }}</p>
    <p v-if="nfc.error">{{ nfc.error }}</p>
  </div>
</template>

<script setup>
import { useNFC } from './useNFC';

const nfc = useNFC();
</script>

最佳实践总结

  • 错误处理: 始终使用 try...catch 块来处理 API 调用中的错误,并向用户显示友好的错误信息。
  • 权限管理: 在调用 navigator.bluetooth.requestDevice()NDEFReader.scan() 之前,检查用户是否已授予相应的权限。
  • UI 反馈: 在执行耗时操作(例如设备连接和数据读取)时,向用户提供 UI 反馈,例如显示加载指示器。
  • 断开连接处理: 监听 gattserverdisconnected 事件,并在设备断开连接时重置状态。
  • 资源清理: 在组件卸载时,清理事件监听器和引用,以防止内存泄漏。
  • 代码模块化: 使用组合式函数来封装 API 调用,提高代码的可重用性和可维护性。
  • 数据验证: 对从蓝牙设备和 NFC 标签读取的数据进行验证,确保数据的有效性。

Web Bluetooth/NFC 与 Vue.js 响应式系统的结合

Web Bluetooth 和 Web NFC API 都是异步的,与 Vue.js 的响应式系统结合的关键在于:

  • 响应式状态管理: 使用 refreactive 创建响应式变量来存储 API 调用结果和设备状态。
  • 异步操作处理: 使用 async/await 来处理异步操作,并在操作完成后更新响应式状态。
  • 计算属性: 使用 computed 创建计算属性来根据响应式状态派生出新的状态,例如设备是否已连接。
  • 副作用处理: 使用 watch 监听状态变化,并在状态变化时执行副作用,例如更新 UI 或发送数据到服务器。

通过这些策略,我们可以将异步操作和状态变化无缝地融入 Vue 的响应式系统中,从而构建更流畅、更直观的用户体验。

案例分析:智能家居控制

我们可以使用 Web Bluetooth 和 Web NFC API 构建一个智能家居控制应用。

  • Web Bluetooth: 用于连接智能灯泡、智能插座等蓝牙设备,并控制它们的开关和亮度。
  • Web NFC: 用于读取 NFC 标签,例如放置在房间门口的 NFC 标签,用户扫描标签后,应用会自动打开该房间的灯。

在这个应用中,设备连接状态、设备数据和 NFC 标签数据都存储在响应式变量中,当这些值发生变化时,Vue 会自动更新 UI,从而实现实时控制和反馈。

展望:未来的可能性

Web Bluetooth 和 Web NFC API 为 Web 应用带来了与物理世界交互的可能性。未来,我们可以利用这些 API 构建更多创新应用,例如:

  • 智能穿戴设备集成: 将智能手表、手环等穿戴设备的数据集成到 Web 应用中。
  • 移动支付: 使用 Web NFC API 实现移动支付功能。
  • 物联网数据可视化: 将物联网设备的数据可视化到 Web 应用中。
  • 位置感知应用: 使用 Web Bluetooth Beacon 技术构建位置感知应用。

这些只是冰山一角,随着技术的不断发展,我们可以期待更多令人兴奋的应用场景。

响应式地管理设备状态和事件

通过使用 Vue.js 的响应式系统,我们可以轻松地管理设备连接状态、数据变化和错误信息,并将其反映在 UI 中。组合式函数可以提高代码的可重用性和可维护性。

更多IT精英技术系列讲座,到智猿学院

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注