# Contact Picker API：让用户分享选定的联系人，而非整个通讯录

> Contact Picker API 的 navigator.contacts.select() 如何让用户挑选特定联系人并选择要分享的字段，为何它要求安全上下文与用户手势，以及为何 MDN 将其标记为实验性且非 Baseline。

**一句话：** Contact Picker API 让 web 应用通过浏览器原生的选择器界面，请求用户从通讯录中挑选一个或多个联系人——只分享用户选中的具体联系人和字段，绝不会分享完整通讯录。

## 为何用选择器而非批量访问

Contact Picker API 允许用户从其联系人列表中选择条目，并将所选条目的有限详情分享给网站
或应用。`navigator.contacts` 暴露一个 `ContactsManager`，`ContactsManager.select()` 会
打开一个由浏览器控制的选择器；用户决定选择哪些联系人，以及释放所请求的哪些属性
（`name`、`email`、`tel`、`address`、`icon`）。MDN 列出的常见用例包括：选择要通过邮件或
聊天应用联系的联系人、为 VOIP 通话选择联系人的电话号码，或发现哪些联系人已在使用某个
社交平台。

## 用法

```js
const props = ['name', 'email', 'tel'];
const opts = { multiple: true };

async function getContacts() {
  try {
    const contacts = await navigator.contacts.select(props, opts);
    handleResults(contacts);
  } catch (ex) {
    // select() 可能抛出异常——见下方「可能出错的地方」。
  }
}
```

`props` 列出要请求的字段；`getProperties()` 会报告当前浏览器实际支持这些字段中的哪些，
让网站在调用 `select()` 之前调整请求：

```js
async function checkProperties() {
  const supported = await navigator.contacts.getProperties();
  if (supported.includes('email')) {
    // 可以安全地在 props 中请求 "email"。
  }
}
```

## 要求

- **安全上下文。** MDN 记载 `select()` 仅在安全上下文（HTTPS）中可用。
- **用户手势。** MDN 说明 `select()` 需要用户手势，返回的 `Promise` 才会 resolve。
- **不适用于 Web Worker。** MDN 指出 Contact Picker API 不会暴露在 `WorkerNavigator` 上。

## 支持情况

MDN 将 Contact Picker API 标记为实验性技术，不属于 Baseline，因为它无法在一些最广泛使用
的浏览器中运行。在生产环境中依赖它之前，请查阅 MDN 上 `Contact_Picker_API` 与
`ContactsManager` 页面的浏览器兼容性表。

## 特性检测

```js
async function shareContacts(props, opts) {
  if (!('contacts' in navigator && 'ContactsManager' in window)) {
    // 此处不支持——回退到手动输入表单。
    return null;
  }
  return navigator.contacts.select(props, opts);
}
```

## 可能出错的地方

MDN 的 `select()` 参考文档记载了该调用可能抛出/拒绝的错误条件：

- **`TypeError`** —— 请求的 `properties` 列表为空，或包含浏览器不支持的属性。
- **`InvalidStateError`** —— 调用发生在非顶层浏览上下文中、已有一个联系人选择器处于打开
  状态，或选择器本身启动失败。
- **`SecurityError`** —— `select()` 并非由用户激活触发。

MDN 自己的示例用 `try`/`catch` 来处理这些情况；由于 `select()` 返回一个 Promise，也可以
用 `.catch()` 处理。

## 实用清单

- [ ] 在调用 `select()` 之前，先用 `'contacts' in navigator && 'ContactsManager' in window`
      做特性检测。
- [ ] 只在真实的用户手势中调用 `select()`——MDN 记载它需要用户手势才能 resolve。
- [ ] 在依赖返回字段之前，先调用 `getProperties()` 确认浏览器实际会返回哪些字段。
- [ ] 处理 `TypeError`、`InvalidStateError`、`SecurityError`——可以用 `try`/`catch`，也
      可以对返回的 Promise 使用 `.catch()`。
- [ ] 准备好手动输入的回退界面——MDN 将该 API 标记为非 Baseline。
- [ ] 不要假设访问权限会持续存在——每次请求都需要用户重新执行一次操作。

## 相关参考

- [Web Push](/zh/reference/notifications/web-push/)
- [通知触发器](/zh/reference/notifications/notification-triggers/)