安装 · 概念
安装提示 UX
发布于
安装提示 UX 是一组决策:页面何时显示自己的安装入口、文案说什么、在不存在程序化提示的地方做什么。只有 Chromium 浏览器(Android 上的 Chrome 68、桌面端 Chrome 73、Edge 79、Samsung Internet 9.0)会给页面一个可以推迟的 beforeinstallprompt 事件;iOS 上的 Safari、macOS Safari 17、Firefox for Android 与 Windows 上的 Firefox 143 只能从各自的菜单安装,页面在那里的任务是描述路径,而不是打开它(BCD api.BeforeInstallPromptEvent)。
页面关于可安装性恰好只有三个信号,所有安装 UX 都由它们构成。
window上的beforeinstallprompt(仅 Chromium)。它的到来证明浏览器认为页面可安装,并会在之后的用户手势中响应prompt()。在它触发之前,安装按钮是页面无法兑现的承诺;调用preventDefault()之后,浏览器自己的横幅会一直隐藏到prompt()被调用。display-mode。matchMedia('(display-mode: standalone)')(以及window-controls-overlay、minimal-ui、fullscreen)告诉页面它已经以安装态运行,这是唯一必须让所有安装入口消失的状态。iOS Safari 还额外暴露旧的布尔属性navigator.standalone。window上的appinstalled(Chromium)。从任何入口完成安装后触发,因此即便用户用的是地址栏图标而不是页面按钮,页面也能隐藏按钮并记录转化。
不写任何页面代码时浏览器显示什么,各平台并不相同,页面文案必须与之对应:
| 浏览器 | 自带安装入口(英文界面) | 程序化提示 |
|---|---|---|
| Chrome,Android | 底部的 mini-infobar,文字为 “Add <应用名> to Home screen”,以及三点菜单中的 “Install app” |
beforeinstallprompt + prompt() |
| Chrome 与 Edge,桌面端 | 地址栏右端的安装图标;菜单项 “Install <应用名>…”(Chrome)或 Apps › “Install this site as an app”(Edge) |
beforeinstallprompt + prompt() |
| Safari,iOS 与 iPadOS | 共享菜单 › “Add to Home Screen”;iOS 26 的面板上有一个默认开启的 “Open as Web App” 开关 | 无 |
| Safari,macOS 14 及以后 | File › “Add to Dock” | 无 |
| Firefox for Android | 三点菜单 › “Install”(或 “Add to Home screen”) | 无 |
| Firefox 143 及以后,Windows | 地址栏图标,把站点固定为任务栏 web 应用 | 无 |
在 Chromium 上,时机不当的提示有明确的代价:userChoice 一旦兑现为 "dismissed",事件对象就已耗尽,Chrome 会施加冷却期后才再次为该站点触发 beforeinstallprompt,所以每次访问页面最多只有一次放对位置的机会。把按钮放在用户完成一项任务之后(保存了表单、读完了文章、第二次会话),用更小的受众换这一次尝试落在意图最强的时刻;放在首屏则触达所有人,却被大多数人取消,这正是 web.dev 安装推广指南记录的权衡。
两个示例都运行在页面中,共用一个回答“是否已安装”的辅助函数。
只在任务完成后显示安装按钮
Section titled “只在任务完成后显示安装按钮”事件到达时保存,但按钮由任务完成的处理器显示,而不是由事件显示。appinstalled 监听器覆盖用户从浏览器自带入口完成的安装。
let deferred = null;const button = document.querySelector('#install');
window.addEventListener('beforeinstallprompt', (event) => { event.preventDefault(); deferred = event; // 先保存;此时不显示任何东西});
function onTaskCompleted() { if (deferred && !isInstalled()) button.hidden = false;}
button.addEventListener('click', async () => { const { outcome } = await deferred.prompt(); deferred = null; button.hidden = true; if (outcome === 'accepted') analytics.track('install_accepted');});
window.addEventListener('appinstalled', () => { deferred = null; button.hidden = true;});
function isInstalled() { return matchMedia('(display-mode: standalone)').matches || navigator.standalone === true;}如果任务完成前事件一直没来(Chrome 的参与度启发式未满足,或浏览器不是 Chromium),deferred 保持 null,按钮保持隐藏,这是正确的行为:一个 prompt() 无法兑现的可见按钮比没有按钮更糟。
判断平台路径,用说明代替按钮
Section titled “判断平台路径,用说明代替按钮”没有事件的地方,页面仍可以告诉用户如何安装,并且一旦应用以 standalone 运行就必须停止。
function installRoute() { if (matchMedia('(display-mode: standalone)').matches || navigator.standalone === true) { return null; // 已安装:什么都不说 } if ('onbeforeinstallprompt' in window) return 'event'; // Chromium:等事件 const ua = navigator.userAgent; if (/iPhone|iPad/.test(ua)) return '点按"共享",然后选择"添加到主屏幕"。'; if (/Macintosh/.test(ua) && /Safari/.test(ua) && !/Chrome/.test(ua)) return '选择"文件",然后选择"添加到程序坞"。'; if (/Android/.test(ua) && /Firefox/.test(ua)) return '打开菜单,然后选择"安装"。'; return null; // 未知:宁可不显示,也不要猜}
const route = installRoute();if (route && route !== 'event') showHint(route);这里的 user agent 判断只用于挑选说明文字,从不用于判断能力;能力来自 'onbeforeinstallprompt' in window 与 display-mode。未知浏览器走 null 分支,避免让用户去找一个并不存在的菜单项。
- How to provide your own in-app install experience(web.dev)
- Patterns for promoting PWA installation(web.dev)
- WebKit features in Safari 26.0(webkit.org)
- beforeinstallprompt 事件
- 可安装性条件
- iOS 添加到主屏幕
- 桌面端安装
规范
| 规范 | 状态 |
|---|---|
| 无。 | |