第一次给项目加 Service Worker 时,我想缓存所有页面、接口和图片,最后得到一套难以解释的规则:有时看到旧内容,有时登出后还能返回缓存页。后来在另一个站点上,我把目标缩小成一句话——用户断网打开页面时,不要只看到浏览器默认错误。
预缓存一张真正独立的离线页
离线页要把必要 CSS 内联或一起预缓存,不能自己又依赖一串网络资源。内容也不需要复杂:说明当前无法连接、建议检查网络,并提供一个重试链接。如果应用里有不依赖网络的功能,可以从这里给入口。
const CACHE = "offline-v1";
const OFFLINE_URL = "/offline.html";
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE).then((cache) => cache.add(OFFLINE_URL))
);
});
注册成功后要实际切换浏览器离线模式测试。开发环境里的自动刷新和代理可能掩盖问题,最好在生产构建上验证一次。
只处理页面导航,其他请求保持原样
最小策略不需要接管 API 和静态资源。监听 fetch 时只判断 request.mode === "navigate",先尝试网络,请求失败才返回缓存的离线页。这样正常在线内容始终来自服务器,也不容易出现陈旧数据。
self.addEventListener("fetch", (event) => {
if (event.request.mode !== "navigate") return;
event.respondWith(
fetch(event.request).catch(() => caches.match(OFFLINE_URL))
);
});
需要注意,HTTP 404 仍然是一次成功的网络响应,不会进入 catch。这正是我们想要的:有网络但地址不存在时,应该显示站点自己的 404,而不是“离线”。
缓存名字就是最简单的版本开关
离线页更新时把缓存名从 offline-v1 改为 offline-v2,并在 activate 阶段删除旧缓存。不要无条件清空整个 Cache Storage,同一域名下可能还有其他功能在使用它。
Service Worker 更新不会立刻控制已经打开的页面。对只提供离线兜底的站点,不必强制刷新用户;下一次访问生效即可。如果功能更关键,再设计清楚的“发现新版本”提示。
这套实现没有离线阅读、后台同步或安装提示,但它解决了最明显的断点,而且几乎不会影响在线逻辑。等真正出现“用户希望离线看最近文章”的需求时,再从这个小起点向前走会更稳。