Service Worker
是HTML5 的一个新特性,主要用来作持久的离线缓存。javascript
在公司业务中,由于常常要处理性能优化方面的需求,使用传统的性能优化手段,知足了大多数业务场景。可是,若是目标用户手机性能以及网络广泛较差的状况下(例如东南亚、印度等海外市场),瓶颈就在于
DNS查询
,TCP
的创建时间,采用常规的优化手段就显得捉襟见肘。此时,咱们项目组有尝试采用离线缓存方案,即将静态资源缓存到本地,经过拦截代理请求,读取本地文件,加快访问速度。html
这个 API 的惟一目的就是解放主线程,
Web Worker
是脱离在主线程以外的,将一些复杂的耗时的活交给它干,完成后经过postMessage
方法告诉主线程,而主线程经过onMessage
方法获得Web Worker
的结果反馈。java
Service Worker
拥有本身独立的 worker
线程,独立于当前网页线程postMessage
向主线程发送消息Promise
异步实现Service Worker
安装(installing
)完成后,就会一直存在,除非手动卸载(unregister
)
Service Worker
的生命周期彻底独立于网页webpack
register
)install
)activate
)一般使用
service worker
只须要如下几个步骤:git
serivceworker
首先,检测当前环境是否支持 service worker
,可使用 'serviceWorker' in navigator
进行检测。github
若是支持,可使用 navigator.serviceWorker.register('./sw.js')
,在当前主线程中注册 service worker
。若是注册成功,service worker
则在 ServiceWorkerGlobalScope
环境中运行; 须要注意的是: 当前环境没法操做DOM
,且和主线程之间相互独立(即线程之间不会相互阻塞)。web
而后,后台开始安装service worker
,通常在此过程当中,开始缓存一些静态资源文件。浏览器
安装成功以后,准备进行激活 service worker
,一般在激活状态下,主要进行缓存清理,更新service worker
等操做。缓存
激活成功后,,service worker
就能够控制当前页面了。须要注意的是,只有在service worker
成功激活后,才具备控制页面的能力,通常在第一次访问页面时,service worker
第一次建立成功,并无激活,只有当刷新页面,再次访问以后,才具备控制页面的能力。安全
本项目在第一次安装serverworker
以后,能够在控制台看到如下信息:
查看对应请求的静态资源信息:
能够看到,第一次安装
serviceworker
时,读取到的静态资源并无缓存。
能够看到,静态资源以及被
serviceworker
缓存起来了。
咱们再来查看当前serviceworker
安装状况:
能够看到,
serviceworker
已经处于激活状态。
最后,看一下离线功能的效果:
是否是很神奇,在离线状态下咱们的页面也是可以展现出数据的。 其中,离线的原理就是利用了
serviceworker
中,fetch
和cacheStorage
这两个接口,将请求进行拦截,将响应进行缓存
该源码实现了如下几个功能:
self.skipWaiting()
,若是检测到新的service worker
文件,就会当即替换掉旧的。cache.addAll(cacheFiles)
经过这个接口实现fetch
事件,能够拦截当前页全部请求self.addEventListener('fetch',function(e){})
cache.put(evt.request, response)
// 缓存静态资源文件列表
let cacheFiles = [
'./test.js',
'./index.html',
'./src/img/yy.png'
]
// serviceworker使用版本
let __version__ = 'cache-v2'
// 缓存静态资源
self.addEventListener('install', function (evt) {
// 强制更新sw.js
self.skipWaiting()
evt.waitUntil(
caches.open(version).then(function (cache) {
return cache.addAll(cacheFiles)
})
)
})
// 缓存更新
self.addEventListener('active', function (evt) {
evt.waitUntil(
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames.map(function (cacheName) {
if (cacheName !== version) {
return caches.delete(cacheName)
}
})
)
})
)
})
// 请求拦截
self.addEventListener('fetch', function (evt) {
console.log('处理fetch事件:', evt.request.url)
evt.respondWith(
caches.match(evt.request).then(function (response) {
if (response) {
console.log('缓存匹配到res:', response)
return response
}
console.log('缓存未匹配对应request,准备从network获取', caches)
return fetch(evt.request).then(function (response) {
console.log('fetch获取到的response:', response)
caches.open(version).then(function (cache) {
cache.put(evt.request, response)
return response
})
})
}).catch(function (err) {
console.error('fetch 接口错误', err)
throw err
})
)
})
复制代码
请参考: 源码地址