主题
02 · PWA 工程落地:Manifest 与 SW 更新
目标:在 Vite 项目落地可安装外壳 + Service Worker 更新提示;缓存策略能讲清;深于 前端进阶 · 18。
1. 背景与目标
上一篇定了「何时选 PWA」。本篇把概念落成可勾选验收的工程步骤。
18 讲清 Cache First / Network First;这里补:文件放哪、怎么注册、发版后用户怎么换新壳。
PWA 最小闭环:
text
HTTPS(或 localhost)
+ Web App Manifest(可安装元数据)
+ Service Worker(可选离线 / 缓存 / 更新)
+ 图标与展示模式1
2
3
4
2
3
4
2. 核心概念
| 概念 | 工程落点 |
|---|---|
manifest.webmanifest | name / short_name / start_url / display / icons / theme_color |
| Service Worker 生命周期 | install → waiting → activate;旧 SW 可能卡在 waiting |
| 预缓存 vs 运行时缓存 | 构建时注入资源列表 vs 请求时按策略写入 |
| 更新 UX | 发现 waiting → UI「有新版本,点击刷新」→ skipWaiting + clients.claim 或整页 reload |
常用策略(与 18 一致,工程默认):
| 资源 | 建议 |
|---|---|
index.html | Network First 或短缓存,避免永远旧壳 |
| 带 hash 的 JS/CSS | Cache First(文件名变了即新资源) |
| API / 用户数据 | 默认不缓存,或 Network First + 明确过期 |
3. 最小实践(Vite)
独立起一个 Vite 玩具项目即可(不必改 Journal)。Windows PowerShell 用分号:
bash
pnpm create vite pwa-demo --template vanilla
cd pwa-demo; pnpm install1
2
2
3.1 Manifest
在 public/manifest.webmanifest:
json
{
"name": "PWA Demo",
"short_name": "PWA",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#111111",
"icons": [
{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
index.html 链上:
html
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="theme-color" content="#111111" />1
2
2
准备两张真实 PNG 图标(可用纯色占位图);缺图标时 Chrome 安装条件可能过不了。
3.2 用 vite-plugin-pwa(推荐)
bash
pnpm add -D vite-plugin-pwa1
vite.config.js 示意:
js
import { defineConfig } from 'vite'
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
VitePWA({
registerType: 'prompt', // 配合「有新版本」提示,勿默默 skipWaiting 除非你清楚后果
manifest: {
name: 'PWA Demo',
short_name: 'PWA',
theme_color: '#111111',
icons: [
{ src: 'icon-192.png', sizes: '192x192', type: 'image/png' },
{ src: 'icon-512.png', sizes: '512x512', type: 'image/png' },
],
},
workbox: {
navigateFallback: '/index.html',
// 按项目收紧 runtimeCaching;API 域名默认不要 Cache First
},
}),
],
})1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
页面里监听更新(插件文档有 virtual:pwa-register 示例):用户点击后再 updateServiceWorker(),然后 reload。
3.3 手写 SW(理解用)
若暂时不用插件,至少手写一次注册,建立心智:
js
// main.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then((reg) => {
reg.addEventListener('updatefound', () => {
const sw = reg.installing
sw?.addEventListener('statechange', () => {
if (sw.state === 'installed' && navigator.serviceWorker.controller) {
// 已有旧控制器 → 新版本在 waiting:弹出「刷新」
}
})
})
})
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
public/sw.js 用 cache.addAll 预缓存外壳即可;生产务必再学 Workbox 或继续用插件,避免手写漏更新。
3.4 验收怎么测
pnpm build; pnpm preview(SW 在非 secure 环境受限,preview/localhost 即可)- DevTools → Application → Manifest / Service Workers
- 改一处文案再 build:应出现 waiting;点刷新后看到新文案
- (可选)Lighthouse → PWA 项扫一眼,不追求满分
4. 踩坑与取舍
- 开发态开着 SW 调试疯了:DevTools 勾 Update on reload,或 Unregister 后再测。
- HTML 强缓存:修了 bug 用户仍旧版——检查 navigate / document 策略。
- 缓存了带 Cookie 的 API:隐私与脏数据风险;默认网络优先。
- 国内「当 App 卖」只靠 PWA:商店与推送能力有限;要进商店看下一篇 Capacitor。
- Journal 小程序:不是浏览器 PWA 模型;别把 SW 经验硬套微信。
默认:有明确「安装到桌面 / 弱网外壳」再上 SW;否则先把加载与错误页做好(与 18 结论一致)。
5. 验收清单
- [ ] 本机 preview 能看到 Manifest,且 SW 已 activated
- [ ] 能口述 HTML vs 带 hash 静态资源各自策略
- [ ] 发版后走过一遍「waiting → 提示 → 刷新 → 新内容」
- [ ] 写明:当前练手项目要不要 PWA,以及理由
- [ ] (可选)Lighthouse PWA 看过一眼
