跳转到内容

安装 · 概念

可安装性条件

发布于

可安装性条件是浏览器为页面提供安装入口(地址栏图标、菜单项或 beforeinstallprompt 事件)之前检查的一组条件。Chromium 浏览器(Chrome、Edge、Samsung Internet)要求安全源,外加带有名称、起始 URL、非 browser 显示模式与图标的 manifest;iOS 26 上的 Safari 与 Windows 上的 Firefox 143 完全没有 manifest 要求,任何页面都能从各自菜单安装(BCD html.manifest、Chrome “Changes to the installability criteria”、WebKit Safari 26.0 发布文章、Firefox 143 发行说明)。

Chromium 在页面每次加载时评估,并把结果记录在 DevTools 的 Application › Manifest › “Installability” 下。按 Chrome 可安装性文章列出的顺序,检查项为:

  • 页面来自安全上下文:https:,或开发期间的 http://localhost 与 http://127.0.0.1。
  • 文档链接了一个可解析的 manifest(<link rel="manifest">),其中包含 name 或 short_name、start_url、取值为 standalone、fullscreen 或 minimal-ui 的 display(或解析到其中之一的 display_override),以及包含 192 px 与 512 px 条目的 icons;安装对话框还要求至少一个 144 px 以上、PNG、SVG 或 WebP 格式、设置了 sizes 且 purpose 包含 any 的图标。
  • start_url 位于 scope 内并返回一个文档(重定向到其他源会使检查失败)。
  • 该应用尚未在当前浏览器配置文件中安装。

带 fetch 处理器的已注册 service worker 曾是清单的一部分,直到 Android 上的 Chrome 108 与桌面端的 Chrome 112 取消了从浏览器菜单安装时的这一要求;Chrome 为没有 service worker 的已安装应用提供默认离线页。同一篇 Chrome 文章指出,beforeinstallprompt 背后的启发式仍然检查 fetch 处理器,所以页面可能通过菜单检查却始终收不到事件。Chrome 还会把事件(以及 Android 的 mini-infobar)推迟到参与度启发式满足之后,这就是 DevTools 面板报告“可安装”而事件不触发的原因。

其他浏览器采用不同的清单:

浏览器 提供安装之前要求什么 安装入口
Edge 79+(桌面端) 与 Chrome 相同的 manifest 与 HTTPS 检查,相同的 Chromium 版本门槛 地址栏图标、Apps 菜单、beforeinstallprompt
Samsung Internet 9.0+ Chromium 清单的自有变体;兼容性数据集记录 beforeinstallprompt 自 9.0 起可用 地址栏安装按钮、beforeinstallprompt
Safari,iOS 与 iPadOS 26 无:从共享菜单添加的每个站点都以 web 应用打开,除非用户关闭 “Open as Web App” 共享菜单 › “Add to Home Screen”
Safari,iOS 与 iPadOS 16.4 至 18 需要 manifest display: standalone 或 fullscreen(或旧的 apple-mobile-web-app-capable meta 标签)才进入 web 应用模式;否则为书签 共享菜单 › “Add to Home Screen”
Safari 17+,macOS 14+ 无;File › “Add to Dock” 对任何页面可用,存在 manifest 时读取其取值 File 菜单
Firefox 143+,Windows 无;地址栏图标把任何站点固定为任务栏 web 应用 地址栏图标
Firefox for Android 一个 manifest;安装为主屏幕快捷方式,无事件 三点菜单 › “Install”

对作者而言,权衡在于只有 Chromium 的清单是可能不通过的,所以满足 Chrome 的 manifest 也满足其余各家浏览器的安装路径;反之不成立,只在 iOS 上测试的页面可能缺少 Chrome 对话框需要的图标就上线了。

第一个示例是镜像 Chromium 清单的页面侧检查;第二个是一份能通过检查的 manifest。

按 Chromium 清单检查页面并据结果行动

Section titled “按 Chromium 清单检查页面并据结果行动”

页面无法运行 Chrome 的可安装性算法,但可以核对它自己控制的输入,并按浏览器决定提供哪种安装界面。回退分支覆盖那些无需任何这些输入就能安装的浏览器。

async function installabilityReport() {
const problems = [];
if (!isSecureContext) problems.push('不是安全上下文:没有浏览器会提供安装');
const link = document.querySelector('link[rel="manifest"]');
if (!link) {
problems.push('没有 <link rel="manifest">');
} else {
const manifest = await fetch(link.href).then((r) => r.json()).catch(() => null);
if (!manifest) problems.push('manifest 无法解析');
else {
if (!manifest.name && !manifest.short_name) problems.push('manifest 需要 name 或 short_name');
if (!manifest.start_url) problems.push('manifest 需要 start_url');
if (!['standalone', 'fullscreen', 'minimal-ui'].includes(manifest.display)) problems.push('display 不能是 browser');
const sizes = (manifest.icons ?? []).flatMap((i) => (i.sizes ?? '').split(' '));
if (!sizes.includes('192x192') || !sizes.includes('512x512')) problems.push('icons 需要 192x192 与 512x512');
}
}
if (!('serviceWorker' in navigator)) problems.push('不支持 service worker:离线页不可用');
return problems;
}
const problems = await installabilityReport();
if (problems.length && 'onbeforeinstallprompt' in window) {
console.warn('Chromium 不会触发 beforeinstallprompt:', problems); // 修 manifest
} else if (!('onbeforeinstallprompt' in window)) {
showMenuInstructions(); // Safari 与 Firefox 不看这份清单,从菜单安装
}

service worker 那一行只是信息提示:自 Chrome 112 起它的缺失不再阻止安装,但意味着已安装应用显示的是 Chrome 的默认离线页,而不是站点自己的。

一份满足所有浏览器安装路径的 manifest

Section titled “一份满足所有浏览器安装路径的 manifest”

下面的 manifest 满足 Chromium 清单;Safari 与 Firefox 会从中读取 name、icons 与 display,但并不要求它们。

{
"name": "Field Notes",
"short_name": "Notes",
"id": "/notes/",
"start_url": "/notes/?source=pwa",
"scope": "/notes/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#1f3a5f",
"icons": [
{ "src": "/notes/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
{ "src": "/notes/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
{ "src": "/notes/icons/maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}

把 any 与 maskable 两种 purpose 放在不同的图标条目上,对 Chrome 对话框很重要:它需要一个 purpose 包含 any 的图标;写成单条 "purpose": "any maskable" 也能通过检查,但两种场景会渲染同一张位图。

规范

规范状态
无。