跳转到内容

安装 · 概念

安装提示 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 都由它们构成。

  1. window 上的 beforeinstallprompt(仅 Chromium)。它的到来证明浏览器认为页面可安装,并会在之后的用户手势中响应 prompt()。在它触发之前,安装按钮是页面无法兑现的承诺;调用 preventDefault() 之后,浏览器自己的横幅会一直隐藏到 prompt() 被调用。
  2. display-mode。matchMedia('(display-mode: standalone)')(以及 window-controls-overlay、minimal-ui、fullscreen)告诉页面它已经以安装态运行,这是唯一必须让所有安装入口消失的状态。iOS Safari 还额外暴露旧的布尔属性 navigator.standalone。
  3. 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 安装推广指南记录的权衡。

两个示例都运行在页面中,共用一个回答“是否已安装”的辅助函数。

事件到达时保存,但按钮由任务完成的处理器显示,而不是由事件显示。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 分支,避免让用户去找一个并不存在的菜单项。

规范

规范状态
无。