没网的时候也能看:给前端页面加一层 Service Worker 做成 PWA
地铁里打开网页白屏、飞机上想看个帮助页看不了——这些糟心事的源头是:普通网页一旦断网,浏览器找不到资源就直接挂。给站点加一层 Service Worker(SW),让它帮你在后台拦截请求、从缓存里兜底,体验立刻不一样。再配个 manifest,还能被"添加到主屏幕"当半个 App 用。这篇把最小可用做法拆开。

一、Service Worker 到底是什么
SW 是跑在浏览器背后的一个独立脚本,不绑定某个页面。它能拦截你站点发出的网络请求,于是你可以决定:这个请求是去网络拿,还是直接返回之前缓存的内容。这就是离线的关键——请求根本没真的发出去,SW 在中间就截住了。
二、注册 + 缓存 app shell
主线程里注册 SW(注意 SW 文件必须在站点根目录或对应作用域下):
// main.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(() => console.log('SW 已注册'))
}
SW 自身用生命周期钩子工作。install 阶段缓存应用外壳(HTML/CSS/JS 这些骨架):
// sw.js
const CACHE = 'v1'
const SHELL = ['/', '/index.html', '/app.js', '/style.css']
self.addEventListener('install', (e) => {
e.waitUntil(caches.open(CACHE).then(c => c.addAll(SHELL)))
})
self.addEventListener('fetch', (e) => {
e.respondWith(
caches.match(e.request).then(cached => cached || fetch(e.request))
)
})
fetch 事件里:缓存命中就直接返回(cache-first),没命中才走网络。断网时所有请求都走缓存分支,页面照常开。
三、manifest 让它像个 App
建一个 manifest.json,浏览器才会弹出"添加到主屏幕":
{
"name": "我的工具",
"short_name": "工具",
"start_url": "/",
"display": "standalone",
"background_color": "#fff",
"icons": [{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" }]
}
在 HTML 头里 <link rel="manifest" href="/manifest.json"> 引用即可。用户加到桌面后,打开是全屏、没有浏览器地址栏,观感接近原生 App。
两种缓存策略对比:
| 策略 | 行为 | 适用 |
|---|---|---|
| cache-first | 先查缓存,没有才网络 | 静态资源、app shell |
| — network-first | 先试网络,失败回缓存 | 需要较新数据的页面 |
| stale-while-revalidate | 立即返缓存,同时后台更新 | 能容忍短暂陈旧的内容 |
几个小提醒
- 作用域限制。SW 文件放
/sw.js才能管整个站;放/sub/sw.js只能管/sub/以下。位置别放错。 - 更新要主动。你改了
sw.js,旧 SW 还在跑。新版本在install后进入waiting,需要skipWaiting()或用户关闭重开才生效。调试时记得在 DevTools 的 Application 面板里手动"Update"或勾"Update on reload"。 - 必须 HTTPS。SW 只在 https(或 localhost 调试)下生效,http 站点用不了,这是浏览器安全限制。
- 缓存版本号记得升。上面
CACHE = 'v1',发版改了资源就改个版本号,旧缓存自然失效,避免用户一直看老页面。 - 别缓存带用户隐私的接口。app shell 缓存没事,但涉及账号的接口走 network-first 甚至不缓存更稳妥。
简单总结一下
PWA 不需要一上来就做得多复杂,先把"SW 缓存 app shell + manifest 可加桌面"这两步做了,离线可用和入口体验就都有了。它不神奇,就是浏览器帮你把"本该去网络拿的东西"提前存了一份。小工具类、文档类、内部系统类站点尤其值得加,用户感知明显。
- 点赞
- 收藏
- 关注作者
评论(0)