service worker的工作机制
1.首先需要加载和安装sw.js
window.onload = () => {
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/sw.js',{ scope: '/js' })
.then(registration => {
console.log('恭喜。作用范围: ', registration.scope);
})
.catch(error => {
console.log('抱歉', error);
});
}
}
- 在onload之后触发是为了页面加载而做的性能优化,因为serviceWorker也需要使用到cpu和内存,所以延迟安装sw.js。
- scope:指定serviceWorker作用的路径,可以理解为serviceWorker的作用域。
- service worker的生命钩子
-
1:
parsed: 注册完成, 脚本解析成功, 尚未安装 -
2:
Install(waiting): 首次安装成功事件或新版本serviceWorker等待安装事件
const CACHE_NAME = 'cache-v1';
const urlsToCache = [
'/',
'/js/main.js',
'/css/style.css',
'/img/bob-ross.jpg',
];
self.addEventListener('install', event => {
self.skipWaiting() // 跳过等待
caches.open(CACHE_NAME)
.then(cache => {
return cache.addAll(urlsToCache);
});
});
- CACHE_NAME:指定service worker中cache storage的名称
- urlsToCache:初始化页面需要缓存的资源列表,若为空数组则初始化不缓存资源
- 当service worker版本存在交替(存在新版本的service worker:既sw.js发生变化时),新版本service worker需要等待(waiting),跳过等待后才能触发新版本的activate。跳过等待需要满足以下任意条件
- 1: 关闭所有往前网站的浏览器tab,在重新打开当前网站,会触发新版本service worker的activate事件
- 2: 在activate事件的回调函数中直接调用self.skipWaiting(): 可直接触发新版本service worker的activate事件。
- 3:
Activate: 事件
- Activate事件再首次安装service worker或新版本service worker安装完成后触发
self.addEventListener('activate', function(event) {
self.clients.claim()
event.waitUntil(
// 获得缓存中所有键
caches.keys().then(function(cacheNames) {
return Promise.all(
// 遍历所有的缓存文件
cacheNames.map(function(cacheName) {
// 若缓存文件不在白名单中,删除之
if (cacheName === CACHE_NAME) {
return caches.delete(cacheName);
}
})
);
})
);
});
- 当
Service Worker被首次注册时,已打开的页面只有在刷新后才会接受Service Worker的控制,如果想要Service Worker在激活后尽快掌握这些页面的控制权,可在activate中调用self.clients.claim方法来实现
- 4:
activated: 激活成功, 可以处理 fetch, message 等事件
- service worker中可以绑定的事件
- install 事件中, 抓取资源进行缓存(在初始化时根据指定的urlsToCache缓存资源)
- 通过前面的urlsToCache指定初始化缓存的资源,urlsToCache为空数组则初始化不缓存任何资源。
- activate 事件中, 遍历缓存, 清除过期的资源
// 通过event.waitUntil删除所有过期的资源
event.waitUntil(
// 获得缓存中所有键
caches.keys().then(function(cacheNames) {
return Promise.all(
// 遍历所有的缓存文件
cacheNames.map(function(cacheName) {
// 若缓存文件不在白名单中,删除之
if (cacheName === CACHE_NAME) {
return caches.delete(cacheName);
}
})
);
})
);
- fetch 事件中, 拦截请求, 查询缓存或者网络, 返回和缓存请求的资源。(在请求时根据代码条件进行缓存)
// 通过fetch事件获取和缓存所有需要缓存的资源。
self.addEventListener('fetch', function(event) {
// 只对 get 类型的请求进行拦截处理
if (event.request.method !== 'GET') {
console.log('WORKER: fetch event ignored.', event.request.method, event.request.url);
return;
}
event.respondWith(
// 缓存中匹配请求
caches.match(event.request)
.then(function(response) {
if (response) {
return response;
}
// 因为 event.request 流已经在 caches.match 中使用过一次,
// 那么该流是不能再次使用的。我们只能得到它的副本,拿去使用。
var fetchRequest = event.request.clone();
// fetch 的通过信方式,得到 Request 对象,然后发送请求
return fetch(fetchRequest).then(
function(response) {
// 检查是否成功
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// 如果成功,该 response 一是要拿给浏览器渲染,而是要进行缓存。
// 不过需要记住,由于 caches.put 使用的是文件的响应流,一旦使用,
// 那么返回的 response 就无法访问造成失败,所以,这里需要复制一份。
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
方式1:基于上面的service woker机制搭建service worker缓存。
- 注意:不要缓存
/sw.js,这样会导致/sw.js无法更新。整个service worker的版本就无法更新。
解决方案:service worker不要缓存index.html,每次构建时给
/sw.${version}js加版本号。在index.html的navigator.serviceWorker.register方法中注入新版本的/sw.${version}js地址。就能及时触发serviceWorker更新。
方式1:基于webpack插件workbox-webpack-plugin
- 具体使用查看workbox-webpack-plugin文档。