Service Workers 是运行在浏览器后台的独立脚本,需手动实现缓存策略与请求拦截以支持离线访问;它不访问 DOM、无同步 API,生命周期由浏览器严格控制,注册后需显式更新并处理 install/fetch/activate 事件。

Service Workers 是浏览器后台运行的脚本,能拦截网络请求、缓存资源、实现离线访问——但它不是“开箱即用”的离线开关,必须手动控制缓存策略和请求响应逻辑。
Service Worker 是什么?和普通 JS 脚本有何不同?
它是一个在 HTTPS(或 localhost)环境下注册的独立 JS 文件,运行在浏览器主线程之外,不访问 window、document 等 DOM 对象,也没有同步 API(如 XMLHttpRequest 同步模式、localStorage)。
关键差异点:
- 生命周期由浏览器控制:安装(
install)、激活(activate)、运行(fetch)阶段严格分离 - 只能通过
navigator.serviceWorker.register()注册,且注册路径必须是其所在目录的“作用域”前缀(例如注册脚本在/sw.js,则作用域默认为/) - 首次注册后不会自动更新;新版本需等待旧版本所有页面关闭、触发
waiting → activating流程才能生效
如何注册并监听 install / fetch 事件?
注册需在主页面 JS 中执行,且应检查兼容性;Service Worker 文件本身需导出事件监听器,不能直接执行逻辑。
立即学习“Java免费学习笔记(深入)”;
主页面中注册示例:
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 基础骨架:
self.addEventListener('install', event => {
event.waitUntil(
caches.open('v1').then(cache => cache.addAll([
'/',
'/index.html',
'/styles.css',
'/app.js'
]))
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});
注意:event.waitUntil() 必须包裹异步操作,否则安装可能失败;caches.open() 返回 Promise,cache.addAll() 会逐个请求并缓存,任一失败则整个安装失败。
为什么页面仍无法离线访问?常见卡点在哪?
即使注册成功、安装完成,离线失败往往源于以下几类问题:
-
fetch事件未覆盖动态资源(如 API 接口),只缓存了 HTML/CSS/JS,导致页面空白或功能异常 - 缓存策略太激进:对带查询参数的请求(如
/api/data?id=123)未标准化 URL,导致每次都被当作新请求跳过缓存 - 未处理
activate阶段的旧缓存清理,多个版本缓存共存可能引发冲突 - 开发时反复改
sw.js但没跳过等待(skipWaiting()),新逻辑实际未激活 - 使用了不支持 Cache API 的老旧浏览器(如 iOS Safari
建议在 activate 中主动清理旧缓存:
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys => {
return Promise.all(keys
.filter(key => key !== 'v1')
.map(key => caches.delete(key))
);
})
);
});
离线应用真正要解决的是“状态一致性”,不是“有没有缓存”
用户刷新页面看到旧 HTML,但 JS 已更新,或 API 返回 404 却渲染了半截数据——这类问题不会因 Service Worker 存在而自动消失。你需要明确:哪些资源必须离线可用(静态资产)、哪些可 fallback(API 请求降级为提示)、哪些需排队重试(表单提交)。这些逻辑都得自己写,Cache API 和 fetch 拦截只是工具。










