Web Worker
浏览器中的 javaScript 都是运行在一个单一主线程上的,在同一时间内只能做一件事情。
Web Worker
是 HTML5 标准的一部分,这一规范定义了一套 API,它允许一段 JavaScript 程序运行在主线程之外的另外一个线程中。
Web Worker
和
Service Worker
有什么关系呢,
Web Worker 是临时的,每次做的事情的结果还不能被持久存下来。
Service Worker 在 Web Worker 的基础上加上了持久离线缓存能力。
Service Worker 缓存 和 http 缓存
- http 缓存
对于
cache-control, Etag, last-modified
等字段我们耳熟能详,这里不过多解释,对于这种缓存还是有缺点的:
- 断网的时候,都不能访问
- 缓存不能编程,不能精细地对需要缓存的内容增删改查
- Service Worker
- 基于 Web Worker ,一个独立的 worker 线程,独立于当前网页进程;
- 让缓存做到优雅和极致
和 PWA 又有什么关系
PWA是为了解决传统Web APP的缺点:
- 没有桌面入口
- 无法离线使用
- 没有Push推送
PWA是前端最火热的一个概念之一,Service Worker为PWA赋能离线可用性以及push消息:
- 和 CacheStorage 搭配,可以做离线应用
- 和 Notification 搭配,可以做像 Native APP 那样的消息推送
- 再加上 Manifest 等,就差不多成了 PWA 了
Service Worker 怎么用
业务需求:假设我当前页有:
- 首页 html
- 一个 get请求
- 若干 js img 请求
[
'./',
'getList',
'img/avatar_v1.jpg',
'js/bundle.js',
]
复制代码
下文将实现对它们的缓存。
简介
Service Worker 可能拥有以下六种状态的一种:
- 解析成功(parsed)
- 正在安装(installing)
- 安装成功(installed)
- 正在激活(activating)
- 激活成功(activated)
- 废弃(redundant)
Parsed
首次注册 Service Worker 时,如果满足 Service Worker 运行的条件(在 https / localhost)环境下。 这也是 第一步:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js', { scope: './' })
.then(function (reg) {
console.log('注册 sw.js 完成');
})
.catch(function (err) {
console.error('注册 sw.js 出错');
})
}
复制代码
Installing
Service Worker 脚本解析完成后,浏览器会试着安装,进入下一状态,“installing”
this.addEventListener('install', function (event) {
console.log('安装 sw.js');
event.waitUntil(
caches.open(CACHE_NAME).then(function (cache) {
return cache.addAll(
[
'./',
'getList',
'img/avatar_v1.jpg',
'js/bundle.js',
]
);
})
)
});
复制代码
我们通常在该环节缓存文件,
event.waitUntil()
方法,则 installing 事件会一直等到该方法中的 Promise 完成之后才会成功;若 Promise 被拒,则安装失败,Service Worker 直接进入废弃(redundant)状态。
激活(Activating)
第一次注册并安装成功后,会触发 activate 事件:
addEventListener('activate', event => {
console.log('安装成功,监听作用域下的所有页面')
})
复制代码
waiting
上面我们介绍的是第一次注册安装完毕, 但是我们缓存的内容不是一成不变的。不可能永远是
[
'./',
'getList',
'img/avatar_v1.jpg',
'js/bundle.js',
]
复制代码
当有sw脚本更新时,在后台默默注册安装新的脚本文件,安装成功后进入 waiting 状态。
这个时候有两种选择:
- 当前所有老版本控制的页面关闭后,再次打开时,新版本的脚本触发 activate 事件
-
手动调用
this.skipWaiting();
基于这一点。 在 activate 我们会处理前后 sw脚本 需要管理的缓存,这里依据当前 sw.js 的缓存名字,判断实现清除之前的缓存。 为什么要处理:
- 除非明确地更新缓存,否则缓存将不会被更新;除非删除,否则缓存数据不会过期
- 浏览器都硬性限制了一个域下缓存数据的大小
this.addEventListener('activate', function (event) {
console.log('激活 sw.js,可以开始处理 fetch 请求。');
event.waitUntil(
caches.keys().then(function (keyList) {
return Promise.all(keyList.map(function (key) {
console.log(CACHE_NAME, key)
if (CACHE_NAME.indexOf(key) === -1) {
/**
* delete() 方法查询request为key的 Cache 条目,如果找到,
* 则删除该 Cache 条目
*/
return caches.delete(key);
}
}))
})
)
});
复制代码
Activated
如果 Service Worker 处于激活态,就可以应对事件性事件 —— fetch。 当然 有必要介绍一下 所有事件如下: 事件:install、activate、message、fetch、push、async。 我们已经学会 三个了。 fetch:处理浏览器发出的请求。
this.addEventListener('fetch', function (event) {
console.log(event.request);
event.respondWith(
caches.match(event.request)
.then(function (resp) {
if (resp) {
console.log(new Date(), 'fetch ', event.request.url, '有缓存,从缓存中取');
return resp;
} else {
console.log(new Date