配了五年移动端,每次分享都要写一串社交平台链接——今天 Web Share API 把这件事彻底原生化了

写过移动端 H5 的人大概都踩过这个坑:产品说加个分享功能,你吭哧吭哧接了微博、微信、QQ、Twitter 四个平台的 SDK,每个都要申请 AppKey、配 URL Scheme、测回调。四年后某个平台改了规则,整个分享模块要重调。

这件事,浏览器自己有个标准接口,两行代码就能覆盖所有场景。


Web Share API 是什么

简单说:让网页调用系统原生的分享面板。用户点了分享按钮,弹出来的是 iMessage、WhatsApp、Airdrop、系统剪贴板——他在手机上每天用几十遍的那些操作,不需要你写一行平台 SDK。

核心两个方法:

// 检查当前数据能否分享(feature guard)
navigator.canShare(data)

// 调起系统分享面板
navigator.share(data)

data 的格式灵活,url/text/title/files 自由组合:

// 分享链接
await navigator.share({
  title: '这篇文章很实用',
  text: '配了五年移动端,今天才发现 Web Share API 这么好用',
  url: location.href
})

// 分享文件(图片、PDF 都行)
await navigator.share({
  files: [new File([blob], 'screenshot.png', { type: 'image/png' })]
})

防御性编程:canShare() 为什么必须先跑

navigator.share() 看起来简单,但它的兼容性是一片雷区:

环境 是否可用
iOS Safari 14+
Android Chrome
Desktop Chrome 89+ ✅(调用系统分享 UI)
Desktop Firefox ❌(截至 2026 年仍不支持)
iOS WebView(微信/钉钉 ❌(安全限制)
非 HTTPS 页面 ❌(API 直接 undefined)

所以不能直接写 navigator.share(),要先把关:

const sharePayload = {
  title: document.title,
  text: '这篇内容不错',
  url: location.href
}

// 第一层判断:API 是否存在
if (navigator.canShare && navigator.canShare(sharePayload)) {
  try {
    await navigator.share(sharePayload)
  } catch (err) {
    // AbortError = 用户打开了面板又取消了,不算错误
    if (err.name !== 'AbortError') {
      console.error('分享失败:', err)
    }
  }
} else {
  // 降级:复制链接到剪贴板
  await navigator.clipboard.writeText(location.href)
  showToast('链接已复制')
}

这一层 guard 的核心价值:不是判断「浏览器支不支持这个 API」,而是判断「这个特定数据在此设备上能不能分享」。比如某些 Android 机型可能支持文字但不支持文件,canShare() 是最后的决定权。


三个移动端躲不开的坑

1. 用户手势必须同步

navigator.share() 必须在按钮点击的同步事件流里触发。如果你在 fetch().then() 的异步回调里调用,浏览器会直接抛出 NotAllowedError

// ✅ 正确:按钮点击同步触发
shareBtn.addEventListener('click', async () => {
  await navigator.share({ url: location.href })
})

// ❌ 错误:异步回调里调用
button.addEventListener('click', () => {
  fetch('/api/data').then(() => {
    navigator.share({ url: location.href }) // 必挂
  })
})

如果必须预加载数据再分享,可以先触发一次用户交互(比如弹确认弹窗),再在弹窗的点击事件里调 share。

2. iOS Safari 跨域 URL 截断

iOS Safari 在分享面板里,如果 URL 和当前页面跨域,系统可能只显示域名、截断 title。这是系统分享面板的行为,代码层无法干预。解法是提前告知用户「分享时确保链接完整」。

3. files 参数的文件格式限制

navigator.share({ files: [...] }) 在 iOS Safari 上只支持图片和少量文档格式;二进制流直接传 File 对象,不要传裸 Blob,并且一定要用 canShare({ files }) 提前验证。


三步落地

第一步:加渐进增强 guard

const canShare = navigator.canShare && navigator.canShare({
  url: location.href
})
shareBtn.textContent = canShare ? '分享' : '复制链接'

第二步:写分享逻辑

async function doShare() {
  const data = { url: location.href, title: document.title }

  if (navigator.canShare?.(data)) {
    try {
      await navigator.share(data)
    } catch (e) {
      if (e.name !== 'AbortError') throw e
    }
  } else {
    await navigator.clipboard.writeText(data.url)
    showToast('已复制到剪贴板')
  }
}

第三步:测三个环境

  • iOS Safari(真机)
  • Android Chrome(真机)
  • Desktop Chrome(看桌面分享面板长什么样)

Web Share API 不是新东西,但它在移动端的生产环境里落地率远低于预期。原因是大多数团队从「接 SDK 分享」迁移到「原生 API 分享」的阻力不大,但这个 API 的坑(尤其是用户手势同步和文件格式限制)需要真正踩过才清楚。下一个你要加分享功能的需求,先问自己一句:真的需要四个平台 SDK 吗?

评论区

0 条评论

登录后可评论。

阿柯·前端架构 13 阅读