PWA装不出去我算了五年,今天发现根子不在产品,在API——canInstall()把安装入口从浏览器手里彻底抢回来了
做了五年的 PWA,每次点「添加到主屏幕」的用户不到 3%——这个数字我盯了很久,直到把 beforeinstallprompt 的局限和 canInstall() + navigator.install() 的新方案全跑了一遍,账才算清楚。
旧方案为什么装不出去
PWA 安装的核心卡点是浏览器那个 mini-infobar——地址栏旁边冒出来的小条,用户点掉的速度比加载还快。行业平均点击率不到 3%,移动端更低,大多数用户根本不知道那个小图标是干什么的。
beforeinstallprompt 事件给了开发者一点点控制权,但限制很清楚——你只能 defer 这个提示,不能主动触发,更不能在用户真正想装的时机弹出。用户在阅读内容,你弹出安装条,体验直接打断。
canInstall() 先问再调,安装按钮不再盲猜
Chrome 143 引入的 Web Install API 带了一个关键能力:canInstall()。在显示任何安装按钮之前,先问浏览器「这个设备能装吗」,答案返回 true,按钮才出现。
const canInstall = await navigator.canInstall?.();
if (canInstall) {
installBtn.style.display = block;
}
这步先问再调的设计,解决了过去最大的盲区——开发者不知道浏览器支不支持、用户有没有装过、是不是隐私模式,直接问系统要答案,按钮出现的那一刻就代表「能装」,不需要自己写一堆兼容性判断。
navigator.install() 把触发时机还给你
有了 canInstall() 打前站,install() 的用法极度简单:
installBtn.addEventListener(click, async () => {
try {
const result = await navigator.install();
console.log(安装成功, result.manifestId);
} catch (e) {
if (e.name === AbortError) {
// 用户取消,什么都不做
} else if (e.name === DataError) {
// manifest 无效,检查 manifest.json
}
// 隐私模式下 promise 直接 reject
}
});
Promise 的错误设计覆盖了所有状态——安装成功、用户拒绝、manifest 找不到、隐私模式——不需要记乱七八糟的事件名,也不需要自己管理 deferredPrompt 变量。
完整的安装体验闭环
把这三件事串起来,就是一个完整的安装体验:
class PWAInstallManager {
constructor() {
this.installBtn = document.getElementById(install-btn);
}
async init() {
// 第一步:问能不能装,不行就隐藏按钮
if (!(await navigator.canInstall?.())) {
this.installBtn.style.display = none;
return;
}
this.installBtn.style.display = block;
this.installBtn.addEventListener(click, () => this.install());
}
async install() {
try {
const { manifestId } = await navigator.install();
this.showSuccess();
} catch (e) {
if (e.name === AbortError) return;
this.showError(e.message);
}
}
}
这个模式的核心变化是:安装按钮的出现时机完全由你控制——用户在阅读页面内容时,按钮可以藏着;当他完成注册、收藏某个内容、或停留超过一定时间,再显示安装入口,转化率完全是另一回事。
配合 Web Share API 的 canShare() 用,效果叠加
Web Share API Level 2 的 canShare() 用了同样的设计逻辑——分享前先问系统能不能处理:
if (navigator.canShare?.({ files: [fileBlob] })) {
shareBtn.classList.add(active);
}
两个 API 用法一致,先问再调,不行就灰掉按钮而不是点了没反应。用户感知到的体验是:出现的就是能用的,不能用的根本不会出来。
浏览器支持现状
navigator.canInstall() 和 navigator.install() 目前在 Chrome/Edge 143+ 默认开启,139-142 版本可通过 about:flags 手动开启。Firefox 和 Safari 目前会忽略这些调用,继续走各自的「添加到主屏幕」路径——所以 canInstall 的判断是必须的,没有这个前置检查,安装按钮点了就是无效操作。
怎么落地
三步走:
第一步,用 canInstall() 替代原来的 beforeinstallprompt 监听,作为显示安装按钮的前置条件。
第二步,在用户完成关键行为后(注册、收藏、停留时长超过阈值)才显示安装按钮,不要首屏就弹。
第三步,安装成功后给出明确的成功反馈,告诉用户「已安装到主屏幕」,而不是只返回一个 manifestId 就完了。
这件事的本质是把安装入口从「地址栏旁边的那个小图标」变成「你页面里一个随时可控的按钮」。用户体验变了,转化率才有可能变。
评论区
登录后可评论。