OpenPWAStore
返回 News
Guide · May 19, 2026

Contact Picker API 需要每次请求时同意,而不仅是用户信任

如何安全地在 PWA 中实施联系人选择且不存储联系人数据。

OpenPWA Editorial2 min read
Contact Picker API 需要每次请求时同意,而不仅是用户信任 cover

为什么联系人选择器对 PWA 很重要

联系人选择器(Contact Picker)让 PWA 能访问用户设备上的联系人,主要用于:

  • 消息应用:选择联系人发送邮件或聊天消息
  • 音视频通话:检索联系人电话号码发起 VoIP 通话
  • 社交网络:发现已加入平台的联系人
  • 协作工具:快速邀请同事加入工作区

MDN 文档强调,此 API 仅在安全顶级浏览上下文中可用,并非常小心地考虑联系人数据的敏感性和隐私

关键设计原则:访问联系人无持久性,用户必须每次请求时授予权限。

如何实施联系人选择器

步骤 1:功能检测

检查浏览器是否支持 Contact Picker API:

const supported = 'contacts' in navigator;

if (!supported) {
  // 提供替代方案:手动输入联系方式、上传 CSV、或跳过此功能
  showFallbackUI();
  return;
}

步骤 2:检查支持的属性

设备可能支持部分联系人属性(如仅名称和电话,不支持地址):

async function getSupportedProperties() {
  const supportedProperties = await navigator.contacts.getProperties();

  return {
    name: supportedProperties.includes('name'),
    email: supportedProperties.includes('email'),
    tel: supportedProperties.includes('tel'),
    address: supportedProperties.includes('address'),
    icon: supportedProperties.includes('icon')
  };
}

步骤 3:请求联系人

使用 navigator.contacts.select() 显示联系人选择器:

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

async function selectContacts() {
  try {
    const contacts = await navigator.contacts.select(props, opts);
    return contacts;
  } catch (ex) {
    console.error('Contact picker failed:', ex);
    return null;
  }
}

关键参数

  • props:要请求的属性数组(nameemailteladdressicon
  • opts.multiple:是否允许多选联系人

返回的 contacts 数组每个元素包含选中的联系人数据:

[{ name: 'John Doe', email: 'john@example.com', tel: '555-1234' }, ...]

步骤 4:处理选中的联系人

async function inviteContacts() {
  try {
    const contacts = await selectContacts();

    if (!contacts || contacts.length === 0) {
      return;
    }

    // 使用联系人数据
    for (const contact of contacts) {
      if (contact.email) {
        sendInvitationEmail(contact.email);
      }
      if (contact.tel) {
        sendSMSInvitation(contact.tel);
      }
    }

    // 重要:不存储联系人数据
    console.log(`Invited ${contacts.length} contacts`);
  } catch (ex) {
    showError('Failed to invite contacts');
  }
}

隐私设计原则

默认不存储

MDN 文档明确指出:

访问联系人无持久性;用户必须每次请求时授予权限。

这意味着:

  • 缓存联系人列表到 localStorage
  • 上传联系人到服务器(除非用户明确同意)
  • 分析联系人数据用于推荐
  • 构建社交图谱

正确做法

  • 仅在当前会话中临时使用联系人数据
  • 处理完成后立即清除引用
  • 需要长期保存时(如邀请列表),只保存用户明确同意保存的项目(如用户手动添加的电话号码,而非从通讯录导入)

最小化数据请求

只请求你真正需要的属性:

// ❌ 坏:请求所有可用属性,过度收集不必要数据
const allProps = ['name', 'email', 'tel', 'address', 'icon'];
navigator.contacts.select(allProps, { multiple: true });

// ✅ 好:只请求语音通话需要的属性
const voiceCallProps = ['name', 'tel'];
navigator.contacts.select(voiceCallProps, { multiple: false });

用户控制权

  • 每次请求都需要用户确认:浏览器显示联系人选择器,用户必须主动选择联系人
  • 单选 vs 多选:根据用例设置 opts.multiple
  • 清晰的目的说明:按钮文本应解释为何访问联系人(如"邀请同事加入")

浏览器兼容性检查清单

在产品化前:

  • [ ] 检查 navigator.contacts 存在性
  • [ ] 测试支持的属性(getProperties()
  • [ ] 测试用户拒绝权限时的行为
  • [ ] 测试空联系人列表场景
  • [ ] 测试联系人数据格式(可选字段可能缺失)

兼容性现状(根据 MDN):

  • Chrome Desktop: 部分支持
  • Chrome Android: 完全支持
  • Edge Desktop/Android: Chrome 同行
  • Safari iOS/Desktop: 不支持
  • Firefox: 不支持

实施检查清单

在发布前:

技术实现

  • [ ] 功能检测并提供回退方案
  • [ ] 异常处理覆盖所有失败场景
  • [ ] 清晰的 UI 反馈(成功/失败/取消)
  • [ ] 不假设任何联系人属性一定存在

用户体验

  • [ ] 按钮标签明确说明用途("邀请联系人" vs "选择联系人")
  • [ ] 提供手动输入联系方式的替代方案
  • [ ] 显示已选择的联系人摘要(不泄露完整数据)
  • [ ] 提供"撤销邀请"功能(如果保存了邀请记录)

隐私合规

  • [ ] 点击触发(非页面加载时自动请求)
  • [ ] 隐私政策说明 Contact Picker 使用
  • [ ] 不存储或上传未明示的联系人数据
  • [ ] 提供清除已使用联系人历史的方法

对开发者意味着什么

产品机会

  • 协作工具 PWA:快速加入联系人,提升转化
  • 社交网络:发现已注册用户,引导注册
  • Messaging 应用:自然选择通信对象

技术债务

  • 兼容性:需要为不支持浏览器提供备选方案
  • UI 实现:联系人选择器由浏览器控制,无法自定义外观
  • 调试困难:联系人数据在真实设备上测试才有意义

用户体验风险

  • 权限疲劳:频繁请求联系人权限可能被用户拒绝
  • 信任问题:用户可能怀疑 PWA 试图"窃取"通讯录
  • 跨场景限制:仅顶级页面可用,iframe 无法使用

决策框架

| 场景 | 使用 Contact Picker | 不使用 Contact Picker | |------|---------------------|----------------------| | 内部协作工具 | ✅ 提升入职效率 | ⚠️ 依靠邮件/链接邀请 | | 社交网络邀请 | ✅ 发现已注册用户 | ⚠️ 改用搜索手机号/邮箱 | | 客服聊天 | ⚠️ 可选性功能 | ✅ 用户直接选择服务 | | e-commerce | ❌ 无关联场景 | ✅ 无需联系人信息 | | 游戏 | ❌ 无关联场景 | ✅ 社交功能不依赖通讯录 | | 医疗/金融 | ⚠️ 需额外合规审查 | ✅ 避免接触敏感数据 |

下一步

  1. 需求审查:确定你的 PWA 真的需要访问联系人,还是可以通过其他方式完成任务
  2. 隐私审查:确保不会存储或上传联系人数据,除非用户明确授权
  3. 原型测试:在 Chrome Android 上测试联系人选择器流程
  4. 用户教育:在首次使用时清晰解释为何需要联系人,增强信任

联系人选择器是强大的能力,但也是极度敏感的隐私入口。透明、克制、尊重用户,才能避免信任危机。