渐进式 Web 应用(PWA)实现指南:将网站变成可安装的 App
不少站长都遇到过这种情况:用户第一次被一篇好文章吸引进来,第二次想回来看更新,却要先翻浏览器历史记录、重新输入网址。PWA(Progressive Web App)要解决的就是这件事——让一个普通网页能被"安装"到桌面或手机主屏幕,拥有独立图标、独立窗口,断网时还能继续浏览已缓存的内容,而实现它只需要一个 manifest 文件加一个 Service Worker。
本文以一个内容站为例,带你走完从配置到上线的全过程。全套代码加起来不到两百行,却能让回访体验和留存率有明显改善。
三个核心组件,缺一不可
PWA 被称为"渐进式",是因为它建立在 Web 既有能力之上,每个组件都是渐进增强,互不依赖:
- manifest.json —— 描述应用名称、图标、启动地址、主题色和显示模式,决定"安装"后的样子;
- Service Worker —— 独立于页面运行的 JavaScript,负责离线缓存、拦截网络请求、推送通知和后台同步;
- HTTPS —— 硬性前提。Service Worker 只能在安全上下文中注册,所以第一步永远是先把 HTTPS 配好,证书类型的选择可以参考 这篇说明。
这里有个常被忽略的点:只有 manifest、Service Worker、HTTPS 三者齐全,Chrome 和 Edge 才会在地址栏弹出完整的"安装应用"提示;只配 manifest,用户还能通过菜单手动添加到主屏幕,但体验会差一截。
第一步:配置 manifest.json
manifest 通常放在站点根目录,以 16IDC 为例:
{
"name": "16IDC",
"short_name": "16IDC",
"description": "Cloud service comparison and website building guide",
"start_url": "/",
"scope": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#4F46E5",
"lang": "zh-CN",
"icons": [
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}
几个字段值得留意:scope 限定 Service Worker 可控制的范围,不写默认是 manifest 所在目录;display: standalone 让应用以独立窗口运行、不带浏览器地址栏;purpose: "any maskable" 对应 Android 的自适应图标,图标四周要预留安全边距,否则会被裁掉一圈。start_url 建议写成站内相对路径,避免部分浏览器安装后打开错误页面。
然后在 <head> 中引用 manifest,并补上 iOS 专属的 apple-touch-icon:
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#4F46E5">
<link rel="apple-touch-icon" href="/icons/icon-192x192.png">
不写最后一行的话,iPhone 上"添加到主屏幕"只会生成一个页面截图缩略图,观感很差。
第二步:注册 Service Worker
在页面加载完成后注册 sw.js:
// main.js
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
try {
const registration = await navigator.serviceWorker.register('/sw.js');
console.log('ServiceWorker registered:', registration.scope);
} catch (error) {
console.log('ServiceWorker registration failed:', error);
}
});
}
建议把注册逻辑放在 load 事件里,避免和页面首屏渲染抢资源。关于前端工程化与框架选型,可参考 前端框架对比。
第三步:Service Worker 缓存策略
缓存策略决定"先读缓存还是先走网络",没有放之四海皆准的方案。下面是四种主流策略的对比:
| 策略 | 请求流程 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| Cache First | 先查缓存,未命中再走网络并回填 | 版本化的 CSS、图片等静态资源 | 秒开、省流量 | 更新有延迟 |
| Network First | 先走网络,失败或超时回退缓存 | 文章详情、新闻列表 | 内容新鲜 | 弱网下稍慢 |
| Stale While Revalidate | 先返回缓存,后台静默更新 | 侧栏、配置类数据 | 即时显示、最终一致 | 逻辑稍复杂 |
| Network Only | 只走网络 | 支付、表单提交 | 数据绝不脏 | 无离线能力 |
一个完整的 sw.js 骨架(包含安装、激活、请求拦截):
// sw.js
const CACHE_NAME = 'v1';
const STATIC_ASSETS = [
'/',
'/styles.css',
'/app.js',
'/offline.html'
];
// 安装:预缓存静态资源
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(STATIC_ASSETS))
.then(() => self.skipWaiting())
);
});
// 激活:清理旧缓存
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys =>
Promise.all(keys.map(key => {
if (key !== CACHE_NAME) return caches.delete(key);
}))
).then(() => self.clients.claim())
);
});
// 拦截请求:缓存优先,兜底离线页
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(cached => cached || fetch(event.request))
.catch(() => caches.match('/offline.html'))
);
});
离线页面与更新机制
把 offline.html 放进预缓存列表后,用户断网访问未缓存的页面时,会被引导到离线页而不是看到浏览器自带的错误页。离线页通常放一个 Logo、一句"网络开小差了"的提示和一个"重试"按钮:
<!DOCTYPE html>
<html>
<head>
<title>您处于离线状态</title>
<style>
body { text-align: center; padding: 50px; font-family: system-ui; }
</style>
</head>
<body>
<h1>暂无网络连接</h1>
<p>请检查您的网络连接后重试</p>
</body>
</html>
另一个常见坑是"更新不生效":Service Worker 安装时会 skipWaiting,但已经打开的旧页面要等下一次加载才会切到新版本。需要强制刷新的话,可以在页面监听 controllerchange 事件,检测到新版本后提示用户刷新。
推送通知与工具检测
推送通知是 PWA 里最依赖第三方服务的部分:需要申请 VAPID 密钥,把订阅信息交给后端,再由后端通过 Web Push 协议下发,与 FCM、APNs 等平台推送深度绑定。如果只是想实现离线可用,这一项完全可以后置。上线前建议用工具做一次体检:
- Lighthouse —— Chrome DevTools 内置的 PWA 审计
- PWABuilder —— Microsoft 的 PWA 打包工具,可生成 Windows/macOS 安装包
- PWA Checker —— 在线 PWA 合规性检测
常见问题
- 一定要 HTTPS 吗? 是。Service Worker 只能在 HTTPS 或 localhost 下注册,这是浏览器硬性要求。
- 图标要准备多大? 至少 192x192 和 512x512 两个尺寸,iOS 再配一个 180x180 的 apple-touch-icon。
- 会影响 SEO 吗? 不会。PWA 本质仍是普通网页,缓存得当反而能加快加载、提升体验分。
- 能上架应用商店吗? 可以。通过 PWABuilder 打包成桌面安装包,Android 也能以 Trusted Web Activity 上架 Google Play。
16IDC 观察
实现成本低(一个 manifest 加一个 Service Worker,一两天就能完成)、收益直观(安装率、回访率、弱网体验都明显提升),PWA 是内容站和工具站性价比最高的体验优化之一。想进一步压榨性能,可以配合 网站性能优化指南 一起做。
参考:Mozilla Web 文档 —— https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps ;Google Web Dev —— https://web.dev/learn/pwa