别再自己画分享图标了:一行代码调起系统分享面板

移动端页面上做「分享」功能,传统做法是自己弹一排图标:微信、微博、QQ,每家接一个 SDK 或拼接一个 scheme。维护成本高,覆盖还不全。其实浏览器早就给了一个更优雅的答案——navigator.share(),一行代码就能调起系统原生的分享面板,微信、备忘录、蓝牙、附近的好友,用户想发给谁就发给谁,你不用关心任何一家的接入细节。

这个 API 官方名称叫 Web Share API,2016 年就有了规范,如今在 iOS Safari 和 Android Chrome 上已经稳定可用多年。手机上刷到过那种点「分享」直接弹出系统级分享菜单的网页,基本就是它。

最小可用示例

先看代码有多简单:

const shareBtn = document.querySelector('#share');

shareBtn.addEventListener('click', async () => {
  try {
    await navigator.share({
      title: document.title,
      text: '推荐一篇好文章',
      url: location.href,
    });
    console.log('分享成功');
  } catch (err) {
    console.log('分享取消或失败:', err);
  }
});

点击按钮,系统分享面板弹出,用户选一个目标完成分享,整个流程结束。share() 返回一个 Promise,注意它在不同平台上的兑现时机不一样:Windows 上分享弹窗启动时就兑现,Android 上则是数据成功传给目标应用之后。所以不要依赖这个 Promise 做「分享成功」的业务统计,它更多是用来捕获异常的。

share() 接受一个对象,成员都是可选的:title(标题)、text(文本)、url(链接)、files(文件数组)。但至少得提供一个,全空或传入浏览器不认识的字段会直接抛 TypeErrortitletext 在多数场景下会被合并成一段内容,具体呈现方式由目标应用决定。

三条硬性前提

这个 API 的限制条件比一般 Web API 多,用之前必须心里有数:

  • 必须由用户手势触发。navigator.share() 不能在页面加载后或定时器里凭空调用,必须在点击、触摸这类「瞬态激活」的事件处理器里执行。这是防滥用设计——没人希望打开一个网页就被强行弹分享面板。
  • 必须是 HTTPS。 Web Share API 只在安全上下文中暴露,本地开发时记得用 localhost,它被豁免。
  • 受权限策略控制。 如果页面跑在 iframe 里,还需要 web-share 权限策略被授予,否则调用会抛 NotAllowedError

分享文件:先问 canShare

分享文件是后来才加入的能力,支持图片、视频、音频、PDF 等常见格式。但各平台对文件类型的支持参差不齐,所以规范给了配套的检测方法 navigator.canShare()

const files = fileInput.files;

if (navigator.canShare({ files })) {
  await navigator.share({
    files,
    title: '看看这张图',
  });
} else {
  console.log('当前环境不支持分享这些文件');
}

canShare() 接受和 share() 一样的参数,返回布尔值。判断文件时只传 { files } 即可。实践中的建议很简单:分享文件前必查 canShare,分享文本前查 navigator.share 是否存在就够了。

降级方案:优雅地退回剪贴板

Web Share API 至今没有进 Baseline——桌面端 Firefox 和部分桌面浏览器仍然不支持。所以特性检测加降级路径是标配写法:

async function share() {
  const data = {
    title: document.title,
    text: '推荐一篇好文章',
    url: location.href,
  };

  if (navigator.share) {
    try {
      await navigator.share(data);
      return;
    } catch (err) {
      // 用户取消分享不是错误,静默处理即可
      if (err.name !== 'AbortError') console.error(err);
      return;
    }
  }

  // 降级:复制链接
  try {
    await navigator.clipboard.writeText(data.url);
    alert('链接已复制,去粘贴给好友吧');
  } catch {
    alert('请手动复制地址栏链接分享');
  }
}

降级方案里最常见的坑是把用户取消也当失败处理。用户在系统面板上点了「取消」,Promise 会以 AbortError 拒绝,这属于正常流程,不该弹出错误提示,更不该重试。

几个实战经验

  • 只在移动端展示分享按钮
    桌面 Chrome 虽然也支持了,但桌面用户的分享习惯更依赖地址栏和 IM,原生面板带来的收益有限。可以结合 pointer: coarse 媒体查询或 UA 判断,只在触屏环境渲染按钮,避免无效 UI。
  • url 会覆盖页面地址 
    传了 url 之后分享出去的就是这个链接而不是当前地址,做「分享指定商品而非当前筛选页」这类需求时特别有用,不用再动 history.pushState
  • iOS 上 text 和 url 会拼在一起
     Safari 的实现里,如果同时传了 texturl,两者会被拼接成一条内容发给目标应用,换行与否由系统决定。微信收到的往往是一段「文案 + 链接」的文本,而不是带卡片的链接消息——想要卡片效果,得靠目标应用自己抓取 url 对应页面的 OG 标签。所以文案别写太长,留足链接的位置。
  • 异常处理看 error.name
    NotAllowedError 多半是没绑在用户手势里,TypeError 是数据有问题,AbortError 是用户取消。按 name 分类处理,比笼统 catch 一个 err 靠谱得多。

写在最后

Web Share API 的价值在于「把分享还给系统」:你只负责把数据准备好,剩下的一切——展示哪些目标应用、传输、完成回调——都交给操作系统处理。代码量从一套自维护的分享矩阵缩减为一个方法调用,体验反而更贴近用户习惯。

如果你的移动端页面还在维护第三方分享 SDK,不妨先用特性检测把 navigator.share 接上,把老方案留作降级路径。

完整规范和兼容性数据请参考 MDN 的 Web Share API 官方文档。

感谢阅读,希望对你有帮助。

 

分享
下一篇 无更多文章