跳到主要内容

场景

2025年01月19日
柏拉文
越努力,越幸运

一、离线支持


将网页的静态资源缓存下来,用户在离线状态下仍能访问网页。

二、消息推送


实现消息推送功能,即使用户未打开网页,也能接收到消息。

三、后台数据同步


在网络恢复后,后台同步数据,例如提交表单或保存状态。

四、资源缓存和管理


资源缓存和管理: 提高网页加载速度,通过缓存策略优化资源使用。在你的 service worker 注册之后,浏览器会尝试为你的页面或站点安装并激活它。install 事件会在注册成功完成之后触发。install 事件通常会这样用,将离线运行 app 产生的资源放置在浏览器离线缓存的空间。为了实现这个,我们使用了 Service Worker 的存储 API——cache—— 一个 service worker 上的全局对象,它使我们可以存储网络响应发来的资源,并且根据它们的请求来生成 key。这个 API 和浏览器的标准的缓存工作原理很相似,但它特定于你的域的。直到你清理它们之前,这些内容都会持久存在。

  1. 新增了一个 install 事件监听器去监听 service worker(这里指的是 self),接着在事件上调用 ExtendableEvent.waitUntil() 方法——这会确保 Service Worker 不会在 waitUntil() 里面的代码执行完毕之前安装完成。

  2. addResourcesToCache() 内,我们使用了 caches.open() 方法来创建了叫做 v1 的新缓存,这将会是我们的站点资源缓存的第 1 个版本。然后我们会在创建的缓存示例中调用 addAll() 函数,它的参数采用一个 URL 数组,指向你想要缓存的所有资源。其中,URL 是相对于 workerlocation

4.1 创建 Service Worker

const addResourcesToCache = async (resources) => {
const cache = await caches.open("v1");
await cache.addAll(resources);
};

self.addEventListener("install", (event) => {
console.log('Service Worker 安装中...');
event.waitUntil(
addResourcesToCache([
"/",
"/index.html",
"/style.css",
"/app.js",
"/image-list.js",
"/star-wars-logo.jpg",
"/gallery/bountyHunters.jpg",
"/gallery/myLittleVader.jpg",
"/gallery/snowTroopers.jpg",
]),
);
});

self.addEventListener('activate', event => {
console.log('Service Worker 激活');
event.waitUntil(
caches.keys().then(keys => {
return Promise.all(
keys.map(key => {
if (key !== 'v1') {
return caches.delete(key);
}
})
);
})
);
});

self.addEventListener('fetch', event => {
console.log('拦截请求:', event.request.url);
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});

4.2 主线程注册 Service Worker

使用 ServiceWorkerContainer.register() 方法首次注册 service worker

const registerServiceWorker = async () => {
if ("serviceWorker" in navigator) {
try {
const registration = await navigator.serviceWorker.register("/sw.js", {
scope: "/",
});
if (registration.installing) {
console.log("正在安装 Service worker");
} else if (registration.waiting) {
console.log("已安装 Service worker installed");
} else if (registration.active) {
console.log("激活 Service worker");
}
} catch (error) {
console.error(`注册失败:${error}`);
}
}
};

registerServiceWorker();

五、网络请求拦截与修改


网络请求拦截与修改: 拦截并修改网络请求,例如提供备用内容或处理失败的请求。

  1. 每次获取 service worker 控制的资源时,都会触发 fetch 事件,这些资源包括了指定的作用域内的文档,和这些文档内引用的其他任何资源(比如 index.html 发起了一个跨源的请求来嵌入一个图片,这个也会通过 service worker)。

  2. 你可以给 service worker 添加一个 fetch 的事件监听器,接着调用 event 上的 respondWith() 方法来劫持我们的 HTTP 响应,然后你用可以用自己的方法来更新它们。caches.match(event.request) 允许我们对网络请求里的每个资源与缓存里可获取的等效资源进行匹配,查看缓存中是否有相应的资源。该匹配通过 URL 和各种标头进行,就像正常的 HTTP 请求一样。

4.1 创建 Service Worker

const addResourcesToCache = async (resources) => {
const cache = await caches.open("v1");
await cache.addAll(resources);
};

self.addEventListener("install", (event) => {
console.log('Service Worker 安装中...');
event.waitUntil(
addResourcesToCache([
"/",
"/index.html",
"/style.css",
"/app.js",
"/image-list.js",
"/star-wars-logo.jpg",
"/gallery/bountyHunters.jpg",
"/gallery/myLittleVader.jpg",
"/gallery/snowTroopers.jpg",
]),
);
});

self.addEventListener('activate', event => {
console.log('Service Worker 激活');
event.waitUntil(
caches.keys().then(keys => {
return Promise.all(
keys.map(key => {
if (key !== 'v1') {
return caches.delete(key);
}
})
);
})
);
});

self.addEventListener('fetch', event => {
console.log('拦截请求:', event.request.url);
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});

4.2 主线程注册 Service Worker

使用 ServiceWorkerContainer.register() 方法首次注册 service worker

const registerServiceWorker = async () => {
if ("serviceWorker" in navigator) {
try {
const registration = await navigator.serviceWorker.register("/sw.js", {
scope: "/",
});
if (registration.installing) {
console.log("正在安装 Service worker");
} else if (registration.waiting) {
console.log("已安装 Service worker installed");
} else if (registration.active) {
console.log("激活 Service worker");
}
} catch (error) {
console.error(`注册失败:${error}`);
}
}
};

registerServiceWorker();