认识 Service Worker

Service Worker 是一个运行在浏览器后台的 JavaScript 脚本,独立于网页主线程,是构建渐进式 Web 应用(PWA)的核心技术。它作为浏览器与网络之间的代理,可以实现离线访问、消息推送、后台同步等原生应用般的功能。

Service Worker 的主要特点:

  • 独立线程:不会阻塞页面渲染,也无法直接操作 DOM。

  • 网络代理:可以拦截页面发出的所有网络请求,并决定如何响应

  • 事件驱动:生命周期由一系列事件(install、activate、fetch 等)驱动

  • 需要 HTTPS:出于安全考虑,Service Worker 只在 HTTPS(或 localhost)环境下生效

js 复制代码
if ("serviceWorker" in navigator) {
  const swUrl = new URL("./sw.js", window.location.href);
  const swScope = new URL("./", window.location.href).pathname;

  navigator.serviceWorker
    .register(swUrl, { scope: swScope })
    .then((reg) => console.log("SW registered", reg.scope))
    .catch((err) => console.error("SW registration failed", err));

  navigator.serviceWorker.ready.then(() => {
    console.log("Service Worker 已就绪");
  });

  navigator.serviceWorker.oncontrollerchange = () => {
    console.log("Service Worker 已激活");
  };
}

async function fetchData() {
  const resultDiv = document.getElementById("result");
  resultDiv.textContent = "正在请求中...";

  try {
    const response = await fetch(
      "https://jsonplaceholder.typicode.com/posts/1"
    );

    if (!response.ok) {
      throw new Error(`HTTP 错误!状态码:${response.status}`);
    }

    const data = await response.json();
    resultDiv.textContent = "请求成功!\n\n" + JSON.stringify(data, null, 2);
  } catch (error) {
    resultDiv.textContent = "请求失败:" + error.message;
    console.error("错误:", error);
  }
}

new URL() 是 JavaScript 内置的构造函数,用于解析和构建 URL 地址。它返回一个 URL 对象,该对象包含 URL 的各个组成部分(如协议、主机名、路径、查询参数等),并提供了方便的属性和方法来操作 URL。

new URL() 构造函数接受两个参数:要解析的 URL 地址和可选的基 URL 地址。如果省略了基 URL 地址,则默认为 undefined

如果 new URL()传入的第一个参数 url 是相对路径,则必须提供 base(基准 URL)来解析成完整地址。

如果传人的第一个参数 url 是绝对路径,则 base 参数将被忽略。

js 复制代码
new URL(url);
new URL(url, base);
js 复制代码
// 使用传入的相对 url 和基 url 构造一个绝对 url
const swUrl = new URL("./sw.js", window.location.href);

serviceWorker.register()scope 参数指定了 Service Worker 能够控制的范围,也就是它能拦截哪些路径下的网络请求。

如果不指定 scope,Service Worker 默认控制脚本所在目录及其子目录。例如,如果脚本在 /js/sw.js,则默认控制 /js/ 及更深的路径(如 /js/page/)。

设置 scope: "/" 表示整个网站(同源下所有页面)都会被该 Service Worker 拦截。如果设置 scope: "/admin/",则只有以 /admin/ 开头的页面和请求会被控制。

scope 不能超出脚本所在的路径层级。即 Service Worker 只能控制与其同目录或子目录的范围,不能控制父目录(除非脚本位于根目录,才能设置 scope: "/")。

js 复制代码
navigator.serviceWorker.register(swUrl, { scope: swScope });

oncontrollerchange 事件在当前页面所关联的 Service Worker 控制器(controller)发生变化时触发。

js 复制代码
navigator.serviceWorker.oncontrollerchange = () => {
  console.log("Service Worker 已激活");
};

Service Worker 有 4 个生命周期,该生命周期由浏览器管理,主要分为以下阶段:

  1. 注册(Registration):页面通过 JavaScript 告诉浏览器 Service Worker 脚本的位置。

  2. 安装(Install):首次注册或版本更新时触发,适合预缓存静态资源。

  3. 激活(Activate):安装成功后触发,可用于清理旧缓存、接管页面。

  4. 空闲/终止:当没有事件需要处理时,浏览器会终止 Service Worker 以节省内存,下次需要时再唤醒。

sw.js 文件内容如下:

js 复制代码
self.addEventListener("install", () => {
  console.log("安装");
  self.skipWaiting(); // 跳过等待,直接激活
});

self.addEventListener("activate", (event) => {
  console.log("激活");
  event.waitUntil(self.clients.claim()); // 立即接管所有页面
});

self.addEventListener("fetch", (event) => {
  console.log("Service Worker 拦截到请求:", event.request.url);
  event.respondWith(fetch(event.request));
});

fetchXMLHttpRequest 的一个很大区别是,fetch 可以在 Service Worker 中使用。

self.skipWaiting() 的作用是让新安装的 Service Worker 跳过 waiting 阶段,尽快进入 active 状态 。

正常流程里,新的 SW 安装后会先 waiting ,要等旧 SW 不再控制任何页面才会激活。

调用 self.skipWaiting() 后,新 SW 不用等那么久,会尝试立即激活。

clients.claim() 让"已激活 SW 立刻接管页面"。

event.waitUntil() 的作用是: 告诉浏览器"这个事件还有异步任务没完成,请先别结束" 。

监听 fetch 事件的作用是:拦截当前 Service Worker 作用域内的所有网络请求,并决定如何返回响应 。

event.respondWith(fetch(event.request)) 把请求原样转发到网络,再把网络结果返回给页面。

event.respondWith()的作用是: 在 fetch 事件里接管这次请求,并指定返回给页面的响应。

总结

Service Worker 是运行在浏览器后台的独立线程脚本,作为浏览器与网络之间的代理,可用于离线访问、消息推送和后台同步等功能。

  • 核心特点:独立线程、事件驱动、可拦截网络请求、仅在 HTTPS(或 localhost)下生效。

  • 注册与路径 :通过 navigator.serviceWorker.register() 注册,常用 new URL() 构造 sw.js 路径;scope 用于限定控制范围,不能超出脚本所在目录。

  • 生命周期:四个阶段 ------ 注册(Registration)、安装(Install,可用于预缓存)、激活(Activate,可清理旧缓存并接管页面)、空闲/终止(节省资源,按需唤醒)。

  • 常用 API 与模式

    • self.skipWaiting()(跳过 waiting,快速激活)、
    • clients.claim()(激活后立即接管页面)、
    • event.waitUntil()(延长事件直至异步任务完成)、
    • fetch 事件中使用 event.respondWith() 拦截并返回响应。
  • 实践示例 :文中示例的 sw.js 展示了 installactivatefetch 的基本写法,以及在 fetch 中将请求透传到网络 event.respondWith(fetch(event.request)) 的简单用法。

以上要点概览了 Service Worker 的作用、注册方式、作用范围、生命周期与常见实现模式,便于快速上手和理解在实践中如何拦截与处理请求。

参考

Using Service Workers

相关推荐
anOnion20 小时前
构建无障碍组件之Menu Button pattern
前端·html·交互设计
用户479492835691520 小时前
claude Fable用不了?把Gpt 5.5pro接到你的claude code里
前端·后端
JieE21220 小时前
LeetCode 101. 对称二叉树|JS 递归 + 迭代双解法,彻底搞懂镜像判断
javascript·算法
冬奇Lab1 天前
AI Workflow 定义的四次演进:从 Markdown 到 JS 脚本,再到分布式多 Agent
javascript·人工智能·agent
zhangxingchao1 天前
Kotlin常用的Flow 操作符整理
前端
IT_陈寒1 天前
React的useState居然还有这种坑?我差点删库跑路
前端·人工智能·后端
Pedantic1 天前
SwiftUI 手势笔记
前端·后端
橙子家1 天前
浏览器缓存之【结构化数据库与缓存】: IndexedDB、Cache storage 和 Storage buckets
前端
user20585561518131 天前
X6 中边悬浮置顶,规避 `mouseleave` 事件丢失问题
前端
李明卫杭州1 天前
CSS aspect-ratio 属性完全指南
前端