service worker
HTML5标准中引入了webworker,它是运行在后台的JavaScript,独立于其他脚本,不会影响页面的性能。Service Worker就是Web Worker的一种实现,充当Web应用程序的本地代理服务器;在特定路径注册Service Worker后,我们可以拦截并处理该路径下的所有网络请求;本文中,我们就是借助于这种代理机制,实现对web页面核心资源可编程式缓存。
基本用法
1. 注册
navigator.serviceWorker.register('your_service_worker.js',{scope: 'your-path-name'}).then(function (registration) {
console.log('succeeded');
}).catch(function (error) {
console.log('error' + error);
});
register 方法接受两个参数,第一个是service worker 的文件路径,第二个是 service worker 的配置项,其中scope 属性控制的是service worker 工作目录,默认为 service worker 所在文件目录。在同一个Origin 下可以注册多个Service Worker,但是Scope不能相同。
2. 注销
navigator.serviceWorker.getRegistration('your_service_worker.js',{scope: 'your-path-name'}).then(function (registration) {
if (registration && registration.unregister) {
registration.unregister().then(function (isUnRegistered) {
if (isUnRegistered) {
console.log('unRegistration succeeded.');
} else {
console.log('unRegistration failed.');
}
});
}
).catch(function (error) {
console.log('[SW]: UnRegistration failed with. ' + error);
});
首先需要通过getRegistration获取service woker,然后调用service woker的unregister 方法进行注销。
3.事件监听
// 安装监听
this.addEventListener('install', function (event) {
console.log('Service Worker install');
});
// 激活监听
this.addEventListener('activate', function (event) {
console.log('Service Worker activate');
});
生命周期
Installing
注册完 service worker 后,浏览器会下载并解析,默认情况下,service worker会24小时被下载一次。
Activating & Activated
在脚本被安装完成后,service worker 会 依次进入 Activating 和 Activated 状态。失败则会进入Redundant 状态。
Redundant
在 Installing 或 Activating 失败后,会进入此状态,在旧 service worker 被新 service Worker 取代后,也会进入此状态。
通信
从页面到service worker
if ('serviceWorker' in window.navigator) {
navigator.serviceWorker.register('your_service_worker.js', { scope:'your-path-name' })
.then(function (reg) {
navigator.serviceWorker.controller && navigator.serviceWorker.controller.postMessage("hello");
});
}
从 service worker 到页面
首先监听,来自页面的消息,根据页面的消息得到event.source 进行相应。
this.addEventListener('message', function (event) {
event.source.postMessage('this message is from sw.js, to page');
});
静态资源缓存
终于到了本文的重点了,在Service worker中可以缓存的包括css、js、图片等在内的几乎全部静态资源。使用service Worker 缓存资源的方式一般为
1.service worker安装成功后开始缓存所需的资源
this.addEventListener('install', function (event) {
event.waitUntil(
caches.open('my_sw_cache').then(function (cache) {
return cache.addAll([
'/',
'/script/style.css',
])
}
));
});
使用 caches.open
方法新建或打开一个已存在的缓存,cache.addAll
方法是请求指定连接的资源并把他们存储到这个缓存中,而使用event.waitUntil
能保证资源被缓存完成前 Service Worker 不会被安装完成,避免发生错误。
2.监听浏览器的所有fetch请求,对已经缓存的资源使用本地缓存进行返回。
this.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
//该fetch请求已经缓存
if (response) {
return response;
}
return fetch(event.request);
}
)
);
});
3. 版本控制
前面说到,有新的 service worker 后,旧版本的页面会被全部关闭,此时就需要我们清理旧的缓存了,如何识别并找出旧版本的缓存就是重点了。而cacheStorage 提供了简单的API,便于我们能查找出旧的缓存资源。其中CACHE_PREFIX是应用的cache前缀,CACHE_VERSION是本次cache的版本号。
function deleteCache() {
return caches.keys().then(function (keys) {
var all = keys.map(function (key) {
if (key.indexOf(CACHE_PREFIX) !== -1 && key.indexOf(CACHE_VERSION) === -1){
console.log('Delete success-->' + key);
return caches.delete(key);
}
});
return Promise.all(all);
});
}
白名单控制
并不是所有的旧的缓存都不需要,有些缓存可以一直使用,所以需要设置一个白名单,当Service Worker 被激活的时候,将不在白名单中的缓存删掉。
const noDelete = ['you_no_delete_source.js'] // 白名单
function deleteCache() {
return caches.keys().then(function (keys) {
var all = keys.map(function (key) {
if (key.indexOf(CACHE_PREFIX) !== -1 && key.indexOf(CACHE_VERSION) === -1 && !noDelete.includes(key)){
console.log('Delete success-->' + key);
return caches.delete(key);
}
});
return Promise.all(all);
});
}
总结
上述代码只是实现了一个demo,实际情况可能复杂多了,比如
1.哪些资源需要缓存,并不是人为控制的,更多情况是webpack打包生成的文件,所以需要生成一套正则匹配规则,用于识别不同的文件,哪些需要缓存,缓存多久。
2.缓存控制不得当,可能获得灾难性后果,若用户得不到正确的相应结果,可能造成页面无响应,数据错乱各种错误。所以需要对响应错误码做额外处理,如避免缓存304,404,xx等不需要缓存的内容。