# Web NFC：从网页读写 NFC 标签

> NDEFReader.scan() 与 write() 如何与 NFC 标签交换 NDEF 消息，规范中的安全上下文与顶层文档可见性要求、"nfc" 权限提示，以及目前从 Chrome for Android 89 开始、非 Baseline 的浏览器支持状况。

**一句话：** 根据 MDN，"Web NFC API 允许通过轻量级的 NFC 数据交换格式（NDEF）消息在 NFC 上交换
数据"。MDN 将其标记为**实验性**，并指出它"不属于 Baseline，因为它在一些广泛使用的浏览器中尚不
可用"。

## 交换的内容

MDN 指出，"设备和标签必须经过专门格式化和写入，以支持 NDEF 记录格式才能与 Web NFC 一起使用"，
并且"该 API 目前不支持低层操作"（不过存在关于添加此类功能的公开讨论）。MDN 的 Web NFC API 概述页面列出了三个接口：`NDEFMessage`（"表示可以从兼容标签接收或发送到兼容
标签的 NDEF 消息"）、`NDEFReader`（"支持从兼容 NFC 标签读取和写入消息"）、`NDEFRecord`（"表示
可以包含在 NDEF 消息中的 NDEF 记录"）。W3C-CG 规范还额外定义了 `NDEFReadingEvent` 接口，用于下文
所述的 `reading` 事件。

## 使用 NDEFReader 读写

```js
const ndef = new NDEFReader();
await ndef.scan();
ndef.onreading = (event) => {
  console.log(`读到 NFC 标签：${event.serialNumber}`);
};
```

根据 MDN，`NDEFReader.scan()` "激活一个读取设备，并返回一个 Promise，该 Promise 在 NFC 标签读取
操作被调度时 resolve，或在遇到硬件或权限错误时 reject"，并且"如果尚未获得 `nfc` 权限，会触发权限
提示"。`NDEFReader.write()` 在写入方面行为相同：它"尝试向标签写入一条 NDEF 消息"，同样会在尚未
获得授权时触发 `nfc` 权限提示。该接口会在"有新的读取结果可用于兼容 NFC 设备时"触发 `reading`
事件，并在"标签靠近读取设备但无法读取时"触发 `readingerror` 事件。

## 安全上下文与文档可见性

Web NFC 规范的安全策略明确规定：**"只有安全上下文才被允许访问 NFC 内容"**——"浏览器可以仅出于
开发目的忽略此规则。" 规范还将访问限制在前台："Web NFC 功能仅允许用于顶层浏览上下文的 Document，
且其 `visibilityState` 为 `'visible'`"，并且"对于处于后台的网页，必须暂停 NFC 内容的接收与写入"。

## 权限模型

规范将 Web NFC 定义为"一个默认的强大特性（powerful feature），由强大特性名称 `nfc` 标识"，其访问
需经过一个"获取权限"算法的把关，该算法会在允许访问前检查该特性当前的权限状态。

## 运行时检测与回退

```js
async function scanNfcTags(onTag) {
  if (!("NDEFReader" in window)) {
    // 此处不支持 Web NFC——改用手动输入或二维码作为回退方案。
    return false;
  }
  const ndef = new NDEFReader();
  await ndef.scan();
  ndef.onreading = onTag;
  return true;
}
```

## 浏览器支持

根据 MDN 的浏览器兼容性数据，`NDEFReader` 的支持从 **Chrome for Android 89 版本**开始，MDN 数据中
WebView for Android 以及其他基于 Chromium 的 Android 浏览器均镜像（mirror）了与 Chrome for Android
相同的支持水平。桌面版 Chrome、Firefox、Safari 均报告不支持（`version_added: false`）。这与 Web NFC
API 概述页面上 MDN 标注的"有限可用性"/"非 Baseline"标签一致。

## 实用清单

- [ ] 使用前先做 `"NDEFReader" in window` 特性检测——MDN 将 Web NFC 标记为实验性且非 Baseline，
      根据 BCD 数据，其支持从 Chrome for Android 89+ 开始。
- [ ] 通过 HTTPS 提供页面：规范规定"只有"安全上下文才被允许访问（并附有明确的仅限开发用途的
      例外）。
- [ ] 不要指望页面进入后台后读写仍会继续——规范要求在顶层文档不可见时暂停 NFC 访问。
- [ ] 只要尚未获得 `nfc` 权限，调用 `scan()` 或 `write()` 就会出现权限提示（根据 MDN）；处理
      因硬件或权限错误而被拒绝的 Promise。
- [ ] 阅读 MDN 的一般性说明——设备和标签"必须经过专门格式化和写入，以支持 NDEF 记录格式"才能
      配合 Web NFC 使用——但注意规范在其基本交互示例中单独列出了"写入未格式化的 NFC 标签"，
      因此不要假设每个标签在写入前都必须已经是 NDEF 格式。

## 相关参考

- [WebHID API](/zh/reference/capabilities/web-hid/)
- [Web Bluetooth](/zh/reference/capabilities/web-bluetooth/)