构建PWA需围绕Web App Manifest、Service Worker和HTTPS三大核心:1. 配置manifest.json定义元数据并正确引入;2. 在根目录注册sw.js,通过install预缓存、fetch拦截实现Cache-First等策略;3. 生产环境强制HTTPS,结合offline.html兜底与DevTools验证确保离线可用。

要从零构建一个可上线的 PWA(Progressive Web App)并实现完整离线能力,核心是三件套:Web App Manifest、Service Worker(含缓存策略)、HTTPS 部署。不依赖框架也能完成,关键是理解每层职责和常见陷阱。
1. 基础配置:声明式元数据(manifest.json)
这是 PWA 被识别为“安装型应用”的第一步。需在 HTML 的 中引入,并确保文件可被公开访问(如放在根目录)。
示例 manifest.json:
{
"name": "我的离线笔记",
"short_name": "笔记",
"description": "支持完全离线使用的轻量笔记应用",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#4285f4",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
注意点:
立即学习“前端免费学习笔记(深入)”;
- 所有路径必须是相对根路径(以 / 开头),否则安装时可能失败
- start_url 必须能返回 200(建议指向 index.html),且该页面需包含
- 图标至少提供 192px 和 512px 两档,PNG 格式最稳妥
2. 离线核心:注册并激活 Service Worker
Service Worker 是 PWA 离线能力的引擎。它是一个运行在浏览器后台的脚本,可拦截网络请求、管理缓存、推送通知。
在主 JS 中注册(例如 main.js):
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('SW registered: ', reg.scope))
.catch(err => console.error('SW registration failed: ', err));
});
}
关键要求:
- sw.js 必须放在网站根目录下(或通过 scope 显式指定),否则无法控制全站
- 页面与 sw.js 必须同源,且网站已启用 HTTPS(本地 localhost 也允许)
- 首次注册后,需刷新一次页面才能触发 install → activate 流程
3. 实现可靠离线:缓存策略 + 回退兜底
仅注册 SW 不等于能离线——必须明确告诉它“缓存什么”和“怎么响应请求”。推荐采用Cache-First + Network-Fallback + 动态缓存组合策略。
典型 sw.js 内容结构:
const CACHE_NAME = 'notes-v1';
const PRECACHE_URLS = [
'/',
'/index.html',
'/styles/main.css',
'/js/app.js',
'/offline.html'
];
self.addEventListener('install', e => {
e.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(PRECACHE_URLS))
.then(() => self.skipWaiting())
);
});
self.addEventListener('activate', e => {
e.waitUntil(
caches.keys().then(keys => {
return Promise.all(keys
.filter(key => key !== CACHE_NAME)
.map(key => caches.delete(key))
);
}).then(() => self.clients.claim())
);
});
self.addEventListener('fetch', e => {
const url = new URL(e.request.url);
// 对 HTML 请求走 Cache + Network fallback(防白屏)
if (e.request.destination === 'document') {
e.respondWith(
caches.match(e.request).then(cached => {
return cached || fetch(e.request)
.then(resp => {
if (resp.status === 200) {
return caches.open(CACHE_NAME)
.then(cache => {
cache.put(e.request, resp.clone());
return resp;
});
}
return resp;
})
.catch(() => caches.match('/offline.html'));
})
);
}
// 对静态资源(CSS/JS/IMG)走 Cache-First
else if (url.origin === location.origin && url.pathname.startsWith('/')) {
e.respondWith(
caches.match(e.request)
.then(cached => cached || fetch(e.request))
);
}
});
说明:
- PRECACHE_URLS 是核心静态资源,install 阶段预加载,确保首次离线即可用
- HTML 页面单独处理:先查缓存,无则发网路请求;成功后自动缓存一份;失败则返回 /offline.html(需提前放入 precache 列表)
- activate 阶段清理旧缓存,避免版本混乱;self.clients.claim() 让新 SW 立即接管当前页
- 不建议对 API 接口做强缓存,可用 Stale-While-Revalidate 或纯网络+错误兜底
4. 上线前必检清单
上线前验证是否真正“可安装、可离线、可更新”:
- 用 Chrome DevTools → Application → Manifest 检查 manifest 是否解析成功、图标是否加载正常
- Application → Service Workers 查看状态:是否 activated、是否有 warning(如 unhandled rejection)
- 勾选 “Offline” 模拟断网,刷新页面,确认首页、核心功能页、图片/CSS/JS 全部加载成功
- 修改 sw.js 内容后保存,刷新两次(第一次更新 SW,第二次激活),检查新逻辑是否生效
- 部署到真实 HTTPS 环境(如 Vercel、Netlify、Nginx 配置 TLS),HTTP 下除 localhost 外无法注册 SW
不复杂但容易忽略。做完这四步,你的 PWA 就具备生产级离线能力了。



















