安装 · API
beforeinstallprompt 事件
发布于 更新于
beforeinstallprompt 是 Chromium 浏览器在页面满足可安装性条件后于 window 上触发的事件;处理器调用 preventDefault() 压住浏览器自己的横幅,保存这个 BeforeInstallPromptEvent,之后在点击中调用 prompt(),让安装对话框在页面选定的时机打开。Android 上的 Chrome 68、桌面端 Chrome 73、Edge 79 与 Samsung Internet 9.0 会触发它;Safari(iOS 与 macOS)与 Firefox 从不触发,只能从各自的菜单安装(BCD api.BeforeInstallPromptEvent)。
window.addEventListener('beforeinstallprompt', (event) => { event.preventDefault(); // 先不让浏览器显示自己的横幅 deferred = event; // 一次性:只能用于一次 prompt()});
const { outcome, platform } = await deferred.prompt(); // 必须在用户手势中// 或:const { outcome, platform } = await deferred.userChoice;
window.addEventListener('appinstalled', () => { /* 从任何界面完成的安装 */ });prompt() 返回的 Promise 与 userChoice 兑现为同一个 { outcome, platform } 对象,二者 await 哪个都可以。无论安装来自 prompt()、地址栏图标还是浏览器菜单,appinstalled 都会在 window 上触发。
BeforeInstallPromptEvent 在 Event 之上扩展了两个成员;appinstalled 是普通的 Event。
| 成员 | 类型 | 说明 |
|---|---|---|
platforms |
readonly string[] |
浏览器提供的安装目标,例如桌面端为 ["web"],Android 上当 manifest 列出 Play 应用且设置 prefer_related_applications 时为 ["web", "play"]。 |
userChoice |
Promise<{ outcome, platform }> |
对话框关闭后兑现。outcome 为 "accepted" 或 "dismissed";platform 是 platforms 中被选中的项,取消时为 ""。 |
prompt() |
Promise<{ outcome, platform }> |
显示浏览器的安装对话框。每个事件只允许调用一次,且必须有瞬时用户激活。 |
preventDefault() |
继承 | 阻止浏览器在这次可安装性检查中显示自己的安装横幅(Android 上的 mini-infobar)。 |
事件何时触发
Section titled “事件何时触发”页面必须通过 HTTPS(或 localhost)提供,并链接一个带有 name 或 short_name、start_url、非 browser 的 display、以及包含 192 px 与 512 px 尺寸图标的 manifest。Chrome 在 Android 上自 Chrome 108、桌面端自 Chrome 112 起取消了从浏览器菜单安装时的 service worker 要求,并提供默认离线页;Chrome 团队关于这次调整的文章指出,beforeinstallprompt 背后的启发式仍然检查 fetch 处理器,因此没有 service worker 的页面可能可以从菜单安装,却始终收不到该事件。应用已安装期间,以及参与度启发式尚未满足时,Chrome 也不会触发它。
只有 prompt() 会拒绝;该 API 中没有别的会抛出。
NotAllowedError:在没有瞬时用户激活的情况下调用了prompt(),例如在页面加载时或定时器中。Chromium 的报错信息为The prompt() method must be called with a user gesture。InvalidStateError:对同一个事件第二次调用prompt(),或事件底层的横幅请求已失效(页面已导航,或浏览器已经显示过自己的界面)。
Safari 与 Firefox 中事件从不触发,所以不存在可被误用的事件对象;假定提示一定会来的代码只是永远不会显示它的按钮。
两个示例都运行在页面中;假定 service worker 与 manifest 已经就位。
推迟事件并从按钮发起提示
Section titled “推迟事件并从按钮发起提示”处理器保存事件并显示按钮。点击处理器调用 prompt()、读取结果,并清空保存的事件,因为它不能复用。appinstalled 监听器负责在用户从浏览器自带界面安装时隐藏按钮。
let deferred = null;const button = document.querySelector('#install');
window.addEventListener('beforeinstallprompt', (event) => { event.preventDefault(); deferred = event; button.hidden = false;});
button.addEventListener('click', async () => { if (!deferred) return; const { outcome, platform } = await deferred.prompt(); deferred = null; // 事件只能用一次 button.hidden = true; if (outcome === 'accepted') analytics.track('install_accepted', { platform });});
window.addEventListener('appinstalled', () => { deferred = null; button.hidden = true;});一次取消的代价不止是丢掉一次点击:Chrome 会对同一站点施加自己的冷却期,之后才再次触发 beforeinstallprompt,所以把按钮放在用户完成一项任务之后、而不是首屏,能让页面拿到的这唯一一次事件用在更可能被接受的时刻。
检测支持,并为 Safari 用户改为给出指引
Section titled “检测支持,并为 Safari 用户改为给出指引”Safari 从不触发该事件。页面仍可以检测自己是否已以安装态运行(display-mode: standalone,或 iOS 上的旧属性 navigator.standalone),若没有,就用分享菜单的说明代替一个永远不会出现的按钮。
const installed = window.matchMedia('(display-mode: standalone)').matches || navigator.standalone === true; // 仅 iOS Safari
const supportsPrompt = 'onbeforeinstallprompt' in window;
if (installed) { hideAllInstallUi();} else if (!supportsPrompt) { // Safari 与 Firefox:不会有事件到来。说明手动路径。 showHint(/iPhone|iPad/.test(navigator.userAgent) ? '点按"共享",然后选择"添加到主屏幕"。' : '请使用浏览器菜单安装此应用。');}// Chromium:等 beforeinstallprompt 触发后再显示任何东西。在所有实现了该事件的 Chromium 浏览器中 'onbeforeinstallprompt' in window 都为 true,所以指引分支只在无法等待事件的地方运行;Chromium 分支则把安装界面一直隐藏到事件确认可安装为止。
- Web Manifest Application Information and incubations: BeforeInstallPromptEvent(wicg.github.io)
- Changes to the installability criteria(developer.chrome.com)
- BeforeInstallPromptEvent(developer.mozilla.org)
- 安装提示浏览器支持
- 可安装性条件
- 安装提示 UX
- iOS 添加到主屏幕
- WebAPK
规范
| 规范 | 状态 |
|---|---|
| 安装提示(beforeinstallprompt) | WICG 草案 |
- 图例
- 支持
- 部分支持
- 需开启标志
- 不支持
- 未知
| 浏览器 / 平台 | 支持 | 版本 | 置信度 | 来源 | 备注 |
|---|---|---|---|---|---|
| Chrome (Android) | 支持 | 68 | 中 | 来源 | — |
| Chrome (Desktop) | 支持 | 73 | 中 | 来源 | — |
| Edge (Desktop) | 支持 | 79 | 中 | 来源 | — |
| Safari (iOS) | 不支持 | — | 中 | 来源 | 1 |
| Safari (macOS) | 不支持 | — | 中 | 来源 | 2 |
| Firefox (Desktop) | 不支持 | — | 中 | 来源 | 3 |
| Samsung Internet | 支持 | 9.0 | 中 | 来源 | — |
- 没有 beforeinstallprompt;需用户通过分享面板 → 添加到主屏幕手动安装。
- 通过「文件 → 添加到程序坞」菜单安装,而不是网页事件。
- 没有基于 manifest 的桌面安装路径。
在线试用
在 OpenPWA 演示应用中运行此能力: /demo/#install