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 个生命周期,该生命周期由浏览器管理,主要分为以下阶段:
-
注册(Registration):页面通过 JavaScript 告诉浏览器 Service Worker 脚本的位置。
-
安装(Install):首次注册或版本更新时触发,适合预缓存静态资源。
-
激活(Activate):安装成功后触发,可用于清理旧缓存、接管页面。
-
空闲/终止:当没有事件需要处理时,浏览器会终止 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));
});
fetch和XMLHttpRequest的一个很大区别是,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展示了install、activate、fetch的基本写法,以及在fetch中将请求透传到网络event.respondWith(fetch(event.request))的简单用法。
以上要点概览了 Service Worker 的作用、注册方式、作用范围、生命周期与常见实现模式,便于快速上手和理解在实践中如何拦截与处理请求。