Service Worker
发布于 更新于
Service Worker 是浏览器在独立 worker 线程中运行的脚本,与任何页面分离。它可以拦截 它所控制页面发出的网络请求并自行响应,这正是离线支持、二次访问秒开与推送得以成立的原因: 标签页关闭之后,这个 worker 仍在运行。
它没有 DOM 访问权限,且只在安全上下文中运行(HTTPS,开发时可用 localhost)。
最小可用的那一个
Section titled “最小可用的那一个”在页面中注册:
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js');} else { // 没有 service worker:站点照常工作,只是没有离线层。}在 /sw.js 中用缓存响应请求,回退到网络:
self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((hit) => hit || fetch(event.request)), );});这就是一个完整可用的 service worker。其余的一切(预缓存、更新策略、过期) 都是在这两块之上的精修。
一次讲完生命周期
Section titled “一次讲完生命周期”注册后的 worker 先 install,再 activate,之后才开始控制页面。在 worker 激活之前
就已加载的页面,要到重新加载后才会被控制。当你发布新的 sw.js,浏览器会在旧 worker 之外
安装它,并让它处于 waiting 状态,直到旧 worker 释放其客户端,这就是为什么一次发布
不会在下一次刷新时立刻生效,除非你显式要求。
见 Service worker 生命周期 与 更新流程与 skipWaiting。
常见的出错点
Section titled “常见的出错点”- 作用域:位于
/js/sw.js的 worker 只控制/js/。把它放在根目录,或发送Service-Worker-Allowed。见注册与作用域。 - 发布后用户仍看到旧版:新 worker 卡在旧 worker 后面等待。
- 不加区分地缓存一切:对 HTML 用 cache-first 会把用户困在旧页面上。按请求类型选择: 缓存策略。
- 当成页面代码去调试:它有自己的生命周期和自己的 DevTools 面板。 见调试 service worker。
本节全部主题
Section titled “本节全部主题”Service worker 生命周期service worker 从 register() 到 redundant 经过哪些状态、每次转换触发什么事件、新版本为何等待,以及 skipWaiting() 改变什么。
Service worker 注册与 scoperegister() 如何把脚本绑定到 URL 前缀 scope,默认 scope 与 Service-Worker-Allowed 如何限定它,重叠如何裁决,以及 updateViaCache。
skipWaiting() 与更新流程浏览器如何检测字节不同的 worker 脚本、新 worker 为何停在 waiting,以及 skipWaiting()、clients.claim()、update() 与 updateViaCache 各改变什么。
FetchEvent 与请求路由service worker 收到的 FetchEvent 成员,respondWith() 与 waitUntil() 的规则及错误结果,以及按 destination、mode 与 URL 路由的写法。
Service worker 缓存策略Cache First、Network First、Stale-While-Revalidate、Network Only、Cache Only 各返回什么、代价是什么,两个陷阱,以及对应的 Workbox 类。
Cache APIcaches 背后的 CacheStorage 与 Cache 接口及其成员,add()、addAll()、put() 抛错的条件,以及 install 与 activate 中的版本化缓存模式。
Clients APIself.clients 暴露的 Clients 接口及其 get()、matchAll()、openWindow()、claim() 成员,抛出 InvalidAccessError 的条件,以及通知点击聚焦窗口的模式。
NavigationPreloadManagerregistration.navigationPreload 如何让导航请求与 service worker 启动并行,它的四个成员与异常条件,以及 preloadResponse 与请求头如何配合。
Service worker 离线兜底请求失败且 URL 无缓存时,service worker 如何返回预缓存的页面、图片或 JSON,Response.error() 做什么,以及 Workbox 的等价写法。
Background Fetch APIBackgroundFetchManager.fetch() 如何把大文件下载交给浏览器显示进度,完成后以哪些事件唤醒 service worker,以及它的异常与失败原因。
Periodic Background Sync APIPeriodicSyncManager.register() 如何按 minInterval 在已安装 PWA 中触发 periodicsync,Chrome 的安装与参与度门槛,以及异常与检测。
WorkboxWorkbox 7 各包做什么,precacheAndRoute() 与 __WB_MANIFEST 如何生成带版本的缓存,GenerateSW 与 InjectManifest 何时适用,以及页面更新。
Service worker 调试Chrome DevTools、Firefox DevTools 与 Safari Web Inspector 在哪里暴露 service worker 状态,如何强制更新、绕过 worker、模拟离线,附面板原文。
Background Sync APISyncManager.register() 如何把失败请求推迟到恢复连接后在 sync 事件中重放,以及它的异常、重试上限与检测方式。
Service Worker 静态路由 APIinstall 事件中的 addRoutes() 如何在 service worker 启动前把匹配请求交给网络、缓存或 fetch 处理器,以及它在什么情况下拒绝。
安全移除 service worker(kill switch)registration.unregister() 移除的是什么、已被控制的页面为何在卸载前仍由旧 worker 接管,以及替换用的 worker 如何清掉自己的缓存。
← 返回参考总览。