You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React PWA中实现RFID标签的读写及读卡器监听功能

React PWA 实现RFID读卡器监听与标签读写方案

第一步:确认读卡器兼容的Web API

首先根据你手上读卡器的类型选择对应的实现路径:

  • 如果是高频NFC类RFID读卡器/标签:优先用Web NFC API,属于W3C标准,PWA在HTTPS环境/localhost下可直接调用,安卓端Chrome 89+原生支持,桌面端多数主流浏览器已适配。
  • 如果是USB接口的自定义协议RFID读卡器:走Web USB API,需要用户主动授权设备访问权限,同样要求HTTPS/localhost运行环境。
  • 如果是普通模拟键盘输出的免驱RFID读卡器:不需要调用特殊API,直接监听全局键盘输入事件即可,这类读卡器读到标签后会自动把卡号按字符依次输出,默认末尾带回车换行符。

第二步:对应场景的实现代码

场景1:模拟键盘输入型读卡器(兼容性最高,无需额外权限)

import { useEffect, useState } from 'react';

function RfidReader() {
  const [rfidCardNo, setRfidCardNo] = useState('');
  let inputBuffer = [];
  // 读卡器输入间隔远小于人工输入,100ms超时即可判断一次输入结束
  const INPUT_TIMEOUT = 100;
  let inputTimer = null;

  useEffect(() => {
    const handleKeyDown = (e) => {
      // 过滤功能键,仅保留可打印字符
      if (e.key.length === 1) {
        clearTimeout(inputTimer);
        inputBuffer.push(e.key);
        inputTimer = setTimeout(() => {
          // 可根据你实际使用的卡片长度调整判断阈值
          if (inputBuffer.length >= 8 && inputBuffer.length <= 16) {
            const cardNo = inputBuffer.join('');
            setRfidCardNo(cardNo);
            // 此处可添加拿到卡号后的业务逻辑
          }
          inputBuffer = [];
        }, INPUT_TIMEOUT);
      }
      // 也可监听读卡器默认输出的回车作为输入结束标志
      if (e.key === 'Enter') {
        clearTimeout(inputTimer);
        if (inputBuffer.length) {
          const cardNo = inputBuffer.join('');
          setRfidCardNo(cardNo);
          inputBuffer = [];
        }
      }
    };

    window.addEventListener('keydown', handleKeyDown);
    return () => {
      window.removeEventListener('keydown', handleKeyDown);
      clearTimeout(inputTimer);
    };
  }, []);

  return <div>当前读到的卡号:{rfidCardNo || '未检测到卡片'}</div>;
}
export default RfidReader;

场景2:NFC类型RFID(Web NFC API实现,支持读写)

import { useEffect, useState } from 'react';

function NfcRfidReader() {
  const [tagData, setTagData] = useState(null);
  const [writeStatus, setWriteStatus] = useState('');

  useEffect(() => {
    if (!('NDEFReader' in window)) {
      console.log('当前浏览器不支持Web NFC');
      return;
    }

    const ndef = new NDEFReader();
    ndef.scan().then(() => {
      ndef.onreading = (event) => {
        const records = event.message.records;
        const decoder = new TextDecoder('utf-8');
        const data = decoder.decode(records[0].data);
        setTagData({
          serialNumber: event.serialNumber,
          content: data
        });
      };
      ndef.onreadingerror = () => console.log('读取标签失败');
    }).catch(err => console.log('启动NFC扫描失败:', err));

    // 标签写入方法
    window.writeNfcTag = async (content) => {
      try {
        await ndef.write({
          records: [{ recordType: 'text', data: content, lang: 'zh' }]
        });
        setWriteStatus('写入成功');
      } catch (err) {
        setWriteStatus(`写入失败:${err.message}`);
      }
    };
  }, []);

  return (
    <div>
      <div>当前标签信息:{tagData ? `卡号:${tagData.serialNumber},内容:${tagData.content}` : '未检测到NFC标签'}</div>
      <button onClick={() => window.writeNfcTag('自定义写入内容')}>写入测试内容</button>
      <div>写入状态:{writeStatus}</div>
    </div>
  );
}
export default NfcRfidReader;

场景3:自定义USB协议RFID读卡器(Web USB API实现)

需提前拿到读卡器的USB VID(厂商ID)、PID(产品ID)、通信协议、输入端点号等参数,替换到对应位置即可:

import { useEffect, useState } from 'react';

function UsbRfidReader() {
  const [device, setDevice] = useState(null);
  const [cardNo, setCardNo] = useState('');
  // 替换为你的读卡器实际VID、PID
  const READER_VID = 0x1234;
  const READER_PID = 0x5678;
  // 替换为你的读卡器实际输入端点号
  const INPUT_ENDPOINT = 0x81;

  const requestDevice = async () => {
    try {
      const usbDevice = await navigator.usb.requestDevice({
        filters: [{ vendorId: READER_VID, productId: READER_PID }]
      });
      await usbDevice.open();
      await usbDevice.selectConfiguration(1);
      await usbDevice.claimInterface(0);
      setDevice(usbDevice);
      startListening(usbDevice);
    } catch (err) {
      console.log('连接读卡器失败:', err);
    }
  };

  const startListening = async (usbDevice) => {
    while (usbDevice.opened) {
      try {
        const result = await usbDevice.transferIn(INPUT_ENDPOINT, 64);
        const decoder = new TextDecoder();
        const rawData = decoder.decode(result.data).trim();
        // 此处按读卡器通信协议解析rawData得到卡号
        if (rawData) {
          setCardNo(rawData);
        }
      } catch (err) {
        console.log('读取数据失败:', err);
        break;
      }
    }
  };

  useEffect(() => {
    if (!('usb' in navigator)) {
      console.log('当前浏览器不支持Web USB');
      return;
    }
    // 页面加载时自动匹配已授权的读卡器
    navigator.usb.getDevices().then(devices => {
      const targetDevice = devices.find(d => d.vendorId === READER_VID && d.productId === READER_PID);
      if (targetDevice) {
        setDevice(targetDevice);
        startListening(targetDevice);
      }
    });
  }, []);

  return (
    <div>
      {!device && <button onClick={requestDevice}>连接RFID读卡器</button>}
      <div>当前读到的卡号:{cardNo || '未检测到卡片'}</div>
    </div>
  );
}
export default UsbRfidReader;

第三步:PWA适配注意点

  • 所有Web API都要求应用运行在HTTPS环境或者localhost下,部署时需满足该要求。
  • 离线使用场景下,将相关页面和JS资源添加到PWA的Service Worker缓存清单即可。
  • Web NFC、Web USB的用户授权会长期保留,后续打开应用无需重复授权。
  • iOS端适配注意:目前iOS Safari仅支持读取NDEF格式的NFC标签,不支持写入,也不支持Web USB,优先选择模拟键盘输入型读卡器可获得最高兼容性。

内容的提问来源于stack exchange,提问作者Artist

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.08 08:06:03