# Web Serial API：navigator.serial 串口访问

> navigator.serial.requestPort() 与 getPorts() 如何授予页面访问串口的权限、目前哪些浏览器支持它，以及如何做特性检测并提供回退。

import CompatTable from '@components/CompatTable.astro';

**一句话：** Web Serial API 让页面通过 `navigator.serial.requestPort()` 请求用户选择一个
串口，然后通过返回的 `SerialPort` 的 `readable`/`writable` 流读写字节。MDN 将 `Serial`
接口标记为**非 Baseline**——"because it does not work in some of the most widely-used
browsers"（因为它在一些广泛使用的浏览器中无法工作）——并且只在**安全上下文**中暴露。

## 请求并复用端口

```js
const port = await navigator.serial.requestPort();
await port.open({ baudRate: 9600 });
```

根据 MDN，`requestPort()` "must be called via transient activation"（必须通过用户激活调用）
——应在点击事件等用户手势中触发，而不是在页面加载时自动调用。`navigator.serial.getPorts()`
会返回当前已连接、且该源已拥有访问权限的端口组成的数组，而不会再次弹出提示：

```js
const ports = await navigator.serial.getPorts();
```

访问也可能被 `serial` 权限策略（Permissions Policy）阻止；根据 MDN，被阻止的页面不会显示
设备选择器，用户也不会看到提示。

## 支持情况

<CompatTable feature="web-serial" />

根据上方的兼容性数据，桌面版 Chrome 和 Edge 自 89 版起支持该 API，桌面版 Firefox 则较晚
才支持，为 151 版起。Safari 在 macOS 和 iOS 上均不支持。在 Android 上，根据 MDN 的
browser-compat-data，Chrome 自 148 版起完整支持；138–146 版的串口"are only available if they are
provided by Bluetooth RFCOMM serial port emulation"（仅在由蓝牙 RFCOMM 串口仿真提供时可用）。
Firefox for Android 不支持该 API。

## 读写数据

```js
const reader = port.readable.getReader();
try {
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    console.log(value);
  }
} finally {
  reader.releaseLock();
}
```

`navigator.serial` 还会派发 `connect` 与 `disconnect` 事件——从 `SerialPort` 冒泡而来
——因此页面可以在已授权设备被接入或移除时做出响应，而不必轮询 `getPorts()`。

## 如何在运行时检测

```js
async function connectSerial() {
  if (!('serial' in navigator)) {
    // 此处不支持 Web Serial —— 回退到 WebUSB，或引导用户使用原生配套应用，
    // 而不是调用 navigator.serial。
    return null;
  }
  const port = await navigator.serial.requestPort();
  await port.open({ baudRate: 9600 });
  return port;
}
```

## 常见问题

- [ ] `requestPort()` 需要用户激活——应在点击事件处理函数中调用，而不是在页面加载时自动
      调用，否则返回的 Promise 会被拒绝。
- [ ] 在调用任何方法之前先做特性检测 `'serial' in navigator`；MDN 将 Web Serial 标记为
      非 Baseline，因此某些浏览器不支持是预期情况。
- [ ] 通过 HTTPS 提供页面——`Serial` 接口只在安全上下文中暴露。
- [ ] 在 Android 上，先确认 Chrome 版本再假设能访问 USB 连接的设备——根据 MDN 的
      browser-compat-data，138–146 版仅覆盖蓝牙 RFCOMM 仿真，148 起为完整支持。
- [ ] 在加载时先调用 `getPorts()`，再回退到 `requestPort()`，以便复用此前已授权的端口
      而无需再次弹出提示。
- [ ] 页面也可能被 `serial` 权限策略直接阻止弹出提示——应将这种失败模式与用户主动拒绝
      区分开来处理。

## 下一步

- [WebUSB API](/zh/reference/capabilities/web-usb/) —— 关于未被操作系统驱动程序占用的
  USB 设备的相关参考条目。
- [WebHID API](/zh/reference/capabilities/web-hid/) —— 关于 USB HID 类输入设备的
  相关参考条目。