导读:本期聚焦于坚哥创作的《如何使用jQuery封装Web NFC API实现NFC标签的读写操作?》,敬请观看详情。直接调用 NDEFReader 而不清理旧监听,是 Web NFC 开发中最常见的一个隐性故障源。页面离开再返回后仍持有之前的 reading 事件,一次贴近标签可能触发多次回调,甚至把旧状态带到下一次写入。本文围绕 jQuery 的 Deferred 模式,将 scan、reading、readingerror 和 write 封装成可复用的异步方法,并说明如何用 AbortController 主动停止扫描、如何在页面可见性变化时清理 NFC 状态。内容涵盖文本记录与 URL 记录的写入、错误分类处理、状态机设计,以及 Android Chrome 下的权限与兼容性约束。jQuery 负责统一 Promise 风格和 DOM 反馈,底层仍使用原生 NDEFReader,适合内部工具和快速原型场景。

Web NFC API 让浏览器可以直接与NFC标签交换NDEF消息,而不依赖原生App。不过它的事件驱动模型比较松散,如果不做二次封装,读写逻辑很容易散落在页面代码里。用 jQuery 的 Deferred 把扫描、错误回调和写入流程统一成 Promise 风格,可以显著降低维护成本。下面先分析原生API的工作方式,再给出封装实现。

如何使用jQuery封装Web NFC API实现NFC标签的读写操作?

NDEFReader 的工作模型与封装切入点

Web NFC API 的核心是 NDEFReader 对象。它同时承担扫描和写入两类职责。读取时调用 scan() 方法启动标签监听,之后通过 reading 事件拿到 NFC 消息;写入时直接调用 write() 方法向靠近的标签写入 NDEF 记录。两个流程都依赖 Secure Context,也就是页面必须通过 HTTPS 或 localhost 访问。

在原生写法中,scan() 返回的 Promise 只表示扫描器是否成功启动,真正的标签数据在后续事件里到达。这种两阶段模型如果用传统回调写,会出现回调嵌套和状态不同步的问题。jQuery 的 Deferred 可以把事件转换成可以被 then、done、fail 订阅的结果。例如在 reading 事件触发时 resolve,在 readingerror 或权限拒绝时 reject,调用方就能像处理普通 Ajax 请求一样处理 NFC 读取。

另一个值得封装的地方是 AbortController。原生 scan() 可以接收 signal 参数,用于主动停止扫描。如果不主动 abort,Android Chrome 的 NFC 扫描会持续占用系统资源,有时还会导致页面切换后再次触发旧监听。封装时应该把 controller 暴露给调用方,确保页面 onHide 或 beforeunload 时能清理。

读取NFC标签:封装 scan 与 reading 事件

读取封装的思路是:每次调用创建一个新的 NDEFReader 和 AbortController,启动扫描后监听 reading 事件,解析 NDEFMessage 中的 records。每个 record 包含 recordType、mediaType、data 等字段,data 可能是字符串、ArrayBuffer 或 URL,需要根据类型做一次统一处理。

下面是一个基于 jQuery Deferred 的读取封装。它返回一个 Promise 对象,调用方可以用 done 接收标签内容,用 fail 捕获权限错误。

(function($) {
  function parseRecord(record) {
    var recordData = record.data;
    if (typeof recordData === 'string') {
      return recordData;
    }
    if (recordData instanceof ArrayBuffer) {
      return new TextDecoder().decode(recordData);
    }
    if (recordData && recordData.buffer) {
      return new TextDecoder().decode(recordData.buffer);
    }
    return String(recordData);
  }

  $.nfcRead = function(options) {
    options = options || {};
    var dfd = $.Deferred();
    if (!('NDEFReader' in window)) {
      dfd.reject(new Error('当前浏览器不支持 Web NFC API'));
      return dfd.promise();
    }

    var reader = new NDEFReader();
    var controller = new AbortController();

    function cleanup() {
      controller.abort();
    }

    reader.scan({ signal: controller.signal }).then(function() {
      dfd.notify({ status: 'scanning' });

      reader.addEventListener('reading', function(event) {
        var serial = event.serialNumber || '';
        var records = Array.from(event.message.records).map(function(record) {
          return {
            recordType: record.recordType,
            mediaType: record.mediaType,
            data: parseRecord(record)
          };
        });
        cleanup();
        dfd.resolve({
          serialNumber: serial,
          records: records
        });
      });

      reader.addEventListener('readingerror', function() {
        cleanup();
        dfd.reject(new Error('读取标签时发生错误'));
      });
    }).catch(function(err) {
      dfd.reject(err);
    });

    return dfd.promise();
  };
})(jQuery);

调用方式也非常直接。页面上放一个 <button id="readNfc">读取 NFC 标签</button>,然后通过 click 事件触发 $.nfcRead。progress 回调可以用来更新界面状态,例如提示用户把标签贴近设备背面。

$('#readNfc').on('click', function() {
  var $status = $('#status');
  $status.text('请将 NFC 标签贴近手机背面');
  $.nfcRead()
    .progress(function(info) {
      $status.text('正在扫描...');
    })
    .done(function(result) {
      console.log('序列号:' + result.serialNumber);
      result.records.forEach(function(item) {
        console.log(item.recordType + ': ' + item.data);
      });
      $status.text('读取成功,共 ' + result.records.length + ' 条记录');
    })
    .fail(function(err) {
      $status.text('读取失败:' + err.message);
    });
});

这里要特别注意 reading 事件并不携带标签的唯一 ID,仅提供 serialNumber 时也要看设备实现。部分 Android 设备对序列号做了随机化处理,不能作为稳定的标签身份凭证。如果业务需要防伪或唯一标识,应该把业务 ID 写入 NDEF 数据区,而不是依赖硬件序列号。

写入NFC标签:用 jQuery Deferred 包装 write 方法

相比读取,写入操作的封装更接近普通异步方法。NDEFReader 的 write() 接受 NDEFMessageSource 参数,可以是一串 NDEFRecord 或完整的 NDEFMessage。写入过程中标签必须保持在感应区域内,一旦距离过远或写入失败,Promise 会进入 rejected 状态。

封装写入时需要考虑 NDEF 记录的构造方式。最简单的文本记录可以直接写成 { records: [{ recordType: 'text', data: 'hello nfc' }] }。如果希望写入 URL 类型,可以使用 recordType: 'url',data 写成 https://ipipp.com。此外,write() 支持 overwrite 选项,默认会覆盖已有标签内容;如果标签是只读的,写入会抛错。

(function($) {
  $.nfcWrite = function(message, options) {
    options = options || {};
    var dfd = $.Deferred();
    if (!('NDEFReader' in window)) {
      dfd.reject(new Error('当前浏览器不支持 Web NFC API'));
      return dfd.promise();
    }

    var reader = new NDEFReader();
    var writeOptions = {
      overwrite: options.overwrite !== false
    };
    if (options.signal) {
      writeOptions.signal = options.signal;
    }

    reader.write(message, writeOptions).then(function() {
      dfd.resolve({ success: true });
    }).catch(function(err) {
      dfd.reject(err);
    });

    return dfd.promise();
  };
})(jQuery);

写入按钮的调用逻辑一般会先构造一条文本记录,再调用 $.nfcWrite。为了让用户知道需要持续贴近设备,可以在调用前显示提示,并在 done 回调里提示写入完成。

$('#writeNfc').on('click', function() {
  var message = {
    records: [
      { recordType: 'text', data: 'jQuery NFC demo' }
    ]
  };
  $('#status').text('请将 NFC 标签贴近设备,写入完成前不要移开');
  $.nfcWrite(message)
    .done(function() {
      $('#status').text('写入成功');
    })
    .fail(function(err) {
      $('#status').text('写入失败:' + err.message);
    });
});

实际测试中,写入 URL 类型比写入 text 类型更容易触发系统对标签格式的校验。如果写入文本没有问题但写入 URL 失败,可以检查标签的容量和是否被厂商锁定。Web NFC API 目前无法解除标签写保护,也无法格式化空白标签为 NDEF 格式,这些限制需要在产品设计时提前评估。

权限、兼容性与工程实践

Web NFC API 在 Chrome for Android 上启用,桌面端和 iOS Safari 暂不支持。调用 scan() 或 write() 前应该先检测 window.NDEFReader 是否存在,同时捕获 NotAllowedError 和 NotSupportedError。权限拒绝通常发生在用户关闭了 NFC 权限或页面不是顶层浏览上下文时,错误消息因版本而异,建议用错误 name 而不是 message 做分支判断。

在工程化封装时,建议把 NFC 能力抽象成一个独立模块,jQuery 只是统一 Promise 风格的壳。模块内部维护扫描状态、事件监听和 AbortController,避免每次点击都创建不可控的监听器。例如可以在页面上只实例化一个 NDEFReader,通过状态机管理 idle、scanning、reading、writing 四种状态,这样多个按钮共享一个读取器,不会出现多个 scan 同时运行的冲突。

var NfcService = (function() {
  var state = 'idle';
  var reader = null;
  var controller = null;

  function ensureReader() {
    if (!('NDEFReader' in window)) {
      throw new Error('浏览器不支持 Web NFC API');
    }
    if (!reader) {
      reader = new NDEFReader();
    }
    return reader;
  }

  function read() {
    if (state !== 'idle') {
      return $.Deferred().reject(new Error('当前有 NFC 操作正在进行')).promise();
    }
    state = 'scanning';
    controller = new AbortController();
    var dfd = $.Deferred();
    var r = ensureReader();
    r.scan({ signal: controller.signal }).catch(function(err) {
      state = 'idle';
      dfd.reject(err);
    });
    r.addEventListener('reading', function(event) {
      state = 'idle';
      dfd.resolve(event.message);
    });
    return dfd.promise();
  }

  function write(message) {
    if (state !== 'idle') {
      return $.Deferred().reject(new Error('当前有 NFC 操作正在进行')).promise();
    }
    state = 'writing';
    var dfd = $.Deferred();
    var r = ensureReader();
    r.write(message).then(function() {
      state = 'idle';
      dfd.resolve({ success: true });
    }).catch(function(err) {
      state = 'idle';
      dfd.reject(err);
    });
    return dfd.promise();
  }

  function stop() {
    if (controller) {
      controller.abort();
      controller = null;
    }
    state = 'idle';
  }

  return { read: read, write: write, stop: stop };
})();

上面的服务层示例展示了状态机如何阻止重复扫描。这种结构比把逻辑全部塞进 $.fn 插件更清晰,jQuery 层只负责 DOM 事件绑定和 UI 反馈。另一个必须处理的是页面可见性变化,用户按 Home 键或切到其他 App 后,Web NFC 扫描可能被系统挂起,回到页面时旧监听可能失效。可以在 visibilitychange 事件里调用 NfcService.stop(),保证状态干净。

安全方面,Web NFC API 只允许读取和写入 NDEF 格式数据,无法访问银行卡支付、身份证件等底层协议。对敏感写入操作,建议在服务端生成签名或校验值,前端只负责传输 NDEF 数据,避免把核心逻辑暴露在客户端脚本里。

最终把上述封装应用在设备管理、现场巡检、智能标签展示等场景中,Web 应用可以直接配合低成本 NFC 标签完成数据采集。相比原生开发,jQuery + Web NFC 方案牺牲了部分底层能力,但换来了更快的迭代速度和跨平台部署能力,适合对实时性要求不高的内部工具和原型验证。

Web NFC APIjQueryNFC标签读写修改时间:2026-10-05 21:30:48

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/1005/66153.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。