跳转到内容

Service Worker 是什么,以及最小可用的那一个

发布于 更新于

Service Worker 是浏览器在独立 worker 线程中运行的脚本,与任何页面分离。它可以拦截 它所控制页面发出的网络请求并自行响应——这正是离线支持、二次访问秒开与推送得以成立的原因: 标签页关闭之后,这个 worker 仍在运行。

它没有 DOM 访问权限,且只在安全上下文中运行(HTTPS,开发时可用 localhost)。

在页面中注册:

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。其余的一切——预缓存、更新策略、过期—— 都是在这两块之上的精修。

注册后的 worker 先 install,再 activate,之后才开始控制页面。在 worker 激活之前 就已加载的页面,要到重新加载后才会被控制。当你发布新的 sw.js,浏览器会在旧 worker 之外 安装它,并让它处于 waiting 状态,直到旧 worker 释放其客户端——这就是为什么一次发布 不会在下一次刷新时立刻生效,除非你显式要求。

见 Service worker 生命周期 与 更新流程与 skipWaiting。

  • 作用域 —— 位于 /js/sw.js 的 worker 只控制 /js/。把它放在根目录,或发送 Service-Worker-Allowed。见注册与作用域。
  • 发布后用户仍看到旧版 —— 新 worker 卡在旧 worker 后面等待。
  • 不加区分地缓存一切 —— 对 HTML 用 cache-first 会把用户困在旧页面上。按请求类型选择: 缓存策略。
  • 当成页面代码去调试 —— 它有自己的生命周期和自己的 DevTools 面板。 见调试 service worker。

← 返回参考总览。