1. service-worker是什么
初次见到sw, 我们会想到什么呢?

上面是一些通用的猜测和想法, 下面我们将进行更为官方的定义.
定义: 一个服务器与浏览器之间的中间人角色,如果网站中注册了service worker那么它可以拦截当前网站所有的请求,进行判断(需要编写相应的判断程序),如果需要向服务器发起请求的就转给服务器,如果可以直接使用缓存的就直接返回缓存不再转给服务器。从而大大提高浏览体验。

如上图所示, C段发出请求, 先被sw捕获, 在sw中判断是否命中缓存, 如有命中缓存, 则返回缓存;
没有则向服务器发请求.(PS: 这里面还涉及到cache-first和net-first策略)
2. service-worker的原理
-
首次看到 Service Worker,我想大家可能会跟我一样都有这东西跟 Web Worker 有什么联系之类的疑问,带着这个疑问让我们来梳理下两者的差异。
-
Web Worker 是现代浏览器提供的一个 JavaScript 多线程解决方案,我们可以将一些复杂、耗时的运算交给 Web Worker 执行以达到释放主线程的目的;Service Worker 则是建立在 Web Worker 之上,旨在通过请求代理、本地缓存、后台同步等机制来提供离线处理能力。两者的主要异同点如下:

3. 生命周期
注册 -> 安装 -> 激活

-
首先我们看下如何注册, 代码如下:
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then((na) => {
console.info('注册成功');
na.onupdatefound = function() {
var e = na.installing;
e.onstatechange = function() {
console.log('e.state =', e.state);
switch (e.state) {
case "installed":
navigator.serviceWorker.controller ? console.log("New or updated content is available.") : console.log("Content is now available offline!");
break;
case "redundant":
console.error("The installing service worker became redundant.")
}
}
}
}).catch((err) => {
console.error('注册失败');
});
navigator.webkitTemporaryStorage.queryUsageAndQuota(console.log);
});
navigator.serviceWorker.addEventListener('message', data => {
console.log('data from service-worker', data);
});
}
-
安装
var version = 4
var CACHE_NAME = "index-image-" + version
let urlsToCache = [
// 'http://localhost:8080/getNews',
'http://localhost:5173/topic/tmp1.json',
'http://localhost:5173/src/main.ts',
'https://pic3.zhimg.com/v2-58d652598269710fa67ec8d1c88d8f03_r.jpg?source=1940ef5c',
]
self.addEventListener('install', function(event) {
//调试时跳过等待过程
self.skipWaiting()
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
return cache.addAll(urlsToCache)
})
)
})
-
激活
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (CACHE_NAME !== cacheName) {
console.log('Deleting out of date cache:', cacheName);
return caches.delete(cacheName);
}
})
);
})
);
});
-
命中缓存
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open(CACHE_NAME).then(function(cache) {
console.log(urlsToCache, event.request.url);
if (!urlsToCache.some(url => event.request.url.includes(url))) {
return fetch(event.request);
}
self.clients.matchAll().then(function(clients) {
clients.forEach(function(client) {
client.postMessage({
msg: 'hello world' + client.id,
source: 'service-worker',
});
});
});
return cache.match(event.request.url.split('?')[0]).then(function(response) {
console.log(response);
if (response) {
return response;
}
throw Error('The cached response that was expected is missing,.');
});
}).catch(function(e) {
console.warn('Couldn\'t serve response for "%s" from cache: %O', event.request.url, e);
return fetch(event.request);
})
);
});
5. 与主线程通信-postMessage
-
主线程接收代码如下:
navigator.serviceWorker.addEventListener('message', data => {
console.log('data from service-worker', data);
});
-
主线程向sw发送消息, sw接收:
window.navigator.serviceWorker.controller.postMessage({
text: '我是来自于主线程的数据'
});
self.addEventListener('message', function(data) {
console.log('data from main-js =', data);
});
6. demo地址