跳转到内容

安装提示 UX:何时、如何展示安装按钮

发布于

一句话: 捕获 beforeinstallprompt,等待有意义的用户信号,再从点击处理器中调用 prompt()——首次加载时就弹出冷启动安装弹窗几乎必然被关掉,并且会浪费你唯一的机会。

let deferredPrompt = null;
// 1. 保存事件;抑制 mini-infobar。
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredPrompt = e;
showInstallAffordance(); // 展示你自己的按钮或横幅
});
// 2. 仅在用户手势中触发。
installButton.addEventListener('click', async () => {
if (!deferredPrompt) return;
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
deferredPrompt = null; // 事件只能使用一次
hideInstallAffordance();
if (outcome === 'accepted') recordInstall();
});
// 3. 安装完成后清理。
window.addEventListener('appinstalled', () => {
deferredPrompt = null;
hideInstallAffordance();
});

关键规则:

  • e.preventDefault() 是阻止 mini-infobar 的唯一方式。
  • prompt() 必须在用户激活上下文(点击、触摸)中调用。在手势外调用会被静默忽略。
  • 已保存的事件是一次性的:一旦 userChoice 解析,同一事件对象无法再次调用 prompt()。 解析后清除引用;如需再次提示,重新监听事件。

beforeinstallprompt 在浏览器的参与度启发式规则满足后触发。 Chrome 也可能在视口底部显示 mini-infobar——preventDefault() 可阻止它。 自定义页内提示让你可以选择合适的时机。用户接受后,Chrome 安装 WebAPK(而非快捷方式) 并触发 appinstalled。

beforeinstallprompt 不会在 Safari(iOS 或 macOS)上触发。不存在编程式安装 API。 唯一的安装路径是:用户点击分享图标 → “添加到主屏幕”。你的 UX 职责是引导用户 (用指向分享图标的箭头或自定义横幅),而非自行触发对话框。 通过 window.navigator.standalone === true 检测应用是否已安装, 以避免向已安装用户显示提示。

beforeinstallprompt 在 Windows、macOS、Linux 和 ChromeOS 上的 Chrome 和 Edge 中触发。安装体验打开一个对话框窗口而非底部弹出表单。地址栏中有一个小型安装图标作为 mini-infobar 的等效物。自定义 prompt() 调用可替代默认流程。安装完成后触发 appinstalled。

Android 版 Firefox 支持安装(Firefox 128+),但不触发 beforeinstallprompt。 不存在 JavaScript 安装 API;用户从浏览器菜单发起安装。桌面版 Firefox 不支持 PWA 安装。

信号 推荐操作
beforeinstallprompt 已触发 显示低调的安装入口(按钮或横幅)——不要用阻断式弹窗
首次页面加载,无参与行为 暂不操作;冷启动提示的接受率极低
用户完成关键任务(结账、读完文章、保存表单) 此时突出显示安装按钮
用户一周内多次打开应用 内联标题芯片或持久底部栏
已安装(display-mode: standalone) 隐藏所有安装 UI
iOS / Safari 在 2–3 次访问后显示“点击分享 → 添加到主屏幕”提示
  • 首次加载时的阻断式弹窗。 Chrome 自身的研究表明,冷启动提示几乎总是被关掉。
  • 被拒后立即再次提示。 多次拒绝后浏览器可能停止触发 beforeinstallprompt; 把每次提示机会视若珍贵。
  • 把提示藏在隐秘的导航里。 让安装入口可见但不突兀。
  • 遗漏 iOS 用户。 beforeinstallprompt 不存在并不意味着 iOS 用户无法安装; 只是需要换一种方式引导他们。
const isStandalone =
window.matchMedia('(display-mode: standalone)').matches ||
window.navigator.standalone === true; // 仅限 Safari iOS
if (isStandalone) hideInstallAffordance();

beforeinstallprompt 的浏览器支持情况各异,请查看 /compatibility/ 获取最新数据。

  • 用 preventDefault() 捕获 beforeinstallprompt 并存储。
  • 仅在事件触发后才显示安装按钮。
  • 在点击处理器内调用 prompt(),不在加载时调用。
  • userChoice 解析后清除延迟事件引用。
  • 监听 appinstalled 以隐藏安装 UI 并记录转化。
  • 独立模式检测可阻止向已安装用户显示 UI。
  • 提供 iOS 回退说明(分享 → 添加到主屏幕)。