大家好啊我是 HOHO。
PWA 这个技术想必大家都听说过,一个可以把浏览器中的网站转化成桌面端应用的功能。
现在市面上有很多给前端项目部署 PWA 的构建插件,很多都是宣称零配置、开箱即用。但是问题就在这里,入门很简单不代表 PWA 本身很简单。恰恰相反,如果对其机制不了解,随便配置就上线的话,很容易出现各种各样的 bug,比如处理起来相当麻烦的过期缓存问题。
而现在网上的很多文章都是拿着插件的文档搞一个入门 start 介绍就结束了,就更容易会对大家造成误解。
如果你是要给自己的项目部署 PWA,但是又觉得有点无从下手,那么本文就非常适合你。我会通过一系列的文章来全面的介绍下 PWA 相关的生态。整个系列大致分为如下五篇文章:
- PWA 开发指南(一):最简单的 PWA 入门配置:如何最低成本的接入 pwa。
- PWA 开发指南(二):service worker 相关生态介绍:(暂未完成) 围绕 sw 诞生的整个生态。包含一大堆的名词解释。
- PWA 开发指南(三):vite-plugin-pwa 部署实践:(暂未完成) 使用插件来工程化的配置 pwa 能力。
- PWA 开发指南(四):避坑指南:(暂未完成) 企业级使用 pwa 时会遇到的一些问题处理。
OK,我们话不多说,直接开始。
了解 PWA 的相关技术
我们先用人话介绍一下 PWA。实际上这个玩意包含两个主要的部件:
- 一个用于指定 PWA 配置(比如名字、logo)的文件,叫做
manifest.webmanifest
。 - 一个前端和后端之间的接口层代理脚本,用于对网站发起的请求进行一些小小的魔改,这个脚本叫做
Service Worker
。
本篇文章我们先讲来讲讲简单的:manifest.webmanifest
。
何为 manifest.webmanifest
manifest.webmanifest
说实话没啥东西,虽然这个后缀名有点奇怪,但是他本质上就是一个 json 文件。下面是一个最简单的配置:
json
{
"name": "my-pwa-app",
"start_url": "/",
"display": "standalone",
"icons": [
{
"src": "/favicon.svg",
"sizes": "any",
"type": "image/svg+xml"
}
]
}
一个名字,一个 logo,没有了。如果你想标准一点,可以用下面这个,具体的配置可以看 这里:
json
{
"$schema": "https://json.schemastore.org/web-manifest-combined.json",
"name": "my-pwa-app",
"short_name": "my-pwa-app",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#ffffff",
"lang": "en",
"scope": "./",
"icons": [
{
"src": "/favicon.svg",
"sizes": "any",
"type": "image/svg+xml"
}
]
}
如果这个文件存在,并且其中指定的 logo 文件也能正常获取到。 那么浏览器就明白了,哦~这确实能用的 PWA 应用。它就会在地址栏里给你显示个按钮来提示用户本网站可以安装为 PWA。

至此,你要做的一切工作其实就已经完成了。你的应用现在已经正式成为一个支持 PWA 的应用了。
有的小伙伴可能会诧异,结束了?这就搞完了?
没错,你在网上看到的什么提供离线访问、预先缓存,其实都是可选项。只要你的网站提供了一份可用的 manifest.webmanifest
。那它就是一个正式的 PWA 应用。只不过现在 PWA 模式下和浏览器模式下的应用一模一样。
试一下
首先
pnpm create vite
创建一个应用,用什么框架都可以。在 public 目录下创建 manifest.webmanifest,然后把上面的 json 复制进去。
最后在 index.html 的 header 添加一行代码:
<link rel="manifest" href="/manifest.webmanifest" />
刷新页面后就可以看到地址栏里的 PWA 安装按钮了。
实际上就算你不配置这个 manifest,也可以手动把网站安装成 PWA。只不过我们通常不认为这是一个"标准的 PWA 应用",并且这种情况下安装出来的应用 LOGO 是默认的,不是很好看:

我的安装按钮没出现!
如果你实践了一下之后发现地址栏里的安装按钮并没有出现,那你可以按照下面的顺序排查一下。
导致按钮不显示的主要有下面三种可能:
manifest.webmanifest
或者 manifest 其中指定的 icons 访问不到 / 返回的响应有问题。manifest.webmanifest
中缺少了关键字段,参考上面最简单配置对比一下。- index.html 里没有 link 引入
manifest.webmanifest
。
第一种情况最常出现,建议不要看代码,直接去浏览器 F12 里看 Network 面板,正常情况下你应该是能看到 manifest.webmanifest
和其中配置的 LOGO 两条 GET 请求的。
如果你发现缺了一条,或者两条都有但是响应是奇奇怪怪的内容(比如一段 html)。那浏览器就不会显示安装按钮。
我的安装按钮出现了但是 LOGO 没有显示!
你也有可能遇到下面的问题,logo 显示的不是你配置的,而是应用名首字母。

这种情况下一般都是 icons 的配置有问题导致的。比如:
- 文件类型不匹配,例如:
src
填的文件后缀名是 .png,但是type
填的是 image/svg+xml。 - 图片格式不标准,例如:不是正方形。
- 图片尺寸和 sizes 不匹配,例如
src
的尺寸是 192x192,但是sizes
填的是 72x72。
你也可以通过 F12 > Application > Manifest > Installability 来看到具体是为什么安装不了:

总之这个图片确实是比较难搞,尤其是 png,有可能你完全没看出来有什么问题,但是就是不显示。所以推荐大家无脑用 svg,不要用 png。
具体的 icons 说明在 这里。
更好的安装页面
眼尖的小伙伴可能在上面的截图里看到了这么一个报错:

这个其实就是在安装时可以配置一些屏幕截图,来给客户更好的介绍你的应用。想要解决这些问题的话,可以在 manifest.webmanifest
里加上这些内容:
json
{
"description": "这是一个超棒的 PWA 演示应用!",
"screenshots": [
{
"src": "screenshot-1.jpg",
"sizes": "2560x1700",
"type": "image/jpg",
"form_factor": "wide",
"label": "Application"
},
{
"src": "screenshot-2.jpg",
"sizes": "2560x1700",
"type": "image/jpg",
"form_factor": "wide",
"label": "Application"
},
{
"src": "screenshot-3.jpg",
"sizes": "2560x1700",
"type": "image/jpg",
"form_factor": "narrow",
"label": "Application"
}
]
}
description
属性是安装时展示的介绍文本,而 screenshots
则是安装时要展示的截图。其中的 form_factor
代表这张截图要展示在哪个平台上:桌面设备(wide)和移动设备(narrow)。注意对应的图片 src 要能访问到哦。
这么配置完之后点击安装按钮就可以看到:

如果你想更详细的进行配置的话,可以参考这篇文档:web.dev/patterns/we...
注意,目前 edge 浏览器不会显示出这些配置的内容。
邀请用户安装 PWA
在配置好 manifest 之后,PWA 应用就已经准备就绪了。我们可以通过浏览器提供的一些 API 来在我们的页面上显示。
核心就是 beforeinstallprompt
事件,这个事件会在浏览器认定网站准备就绪可以安装时触发。这个时间会返回一个对象,对象上暴露了两个属性:
.prompt()
:调用后会立刻展示安装弹窗。.userChoice
:一个 promise,会在用户同意或者拒绝后触发。
很简单对吧,比如下面这个 demo:
html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + TS</title>
<link rel="manifest" href="/manifest.webmanifest" />
</head>
<body>
<div id="app" style="display:none;">
<h1>🎉 PWA 已经就绪</h1>
<div class="card">
<button id="add-btn">点此安装</button>
</div>
</div>
</body>
<script>
let deferredPrompt;
const addBtn = document.querySelector("#add-btn");
const app = document.querySelector("#app");
window.addEventListener("beforeinstallprompt", (e) => {
e.preventDefault();
deferredPrompt = e;
// 更新 UI 以通知用户可以添加到主屏幕
app.style.display = "inline-block";
});
addBtn.addEventListener("click", (e) => {
// 隐藏我们的用户界面,显示安装提示
addBtn.innerHTML = "用户正在做出选择";
// 显示之前保存的提示
deferredPrompt.prompt();
// 等待用户的响应以决定是否安装应用程序
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === "accepted") {
console.log("用户接受了安装提示");
} else {
console.log("用户拒绝了安装提示");
}
addBtn.innerHTML = "点此安装";
deferredPrompt = null;
});
});
</script>
</html>
跑起来就是这样的:

不过说实话,这个用的人并不是很多,而且这个特性并没有达成标准,目前只有 edge 和 chrome 支持,firefox 和 safari 都是不支持的,大家了解一下就好。相关文档可以看 这里。
小结
本篇文章简单介绍一下如何启用 pwa 安装,没什么技术含量。如果你只是因为客户的需求想添加这么一个按钮,那按照本篇文章搞一下就行了,省时省力。
如果你想深入研究一下相关技术,或者应用要求需要比较硬的 pwa 能力的话,我在下一篇等你。