目前,service worker已经作为草案被提出,可以实现消息推动、地理围栏、离线应用等功能,相当于在浏览器端建立了一个代理服务,实现一些现在看来逆天的功能。
1. Service worker介绍
目前原生App跟HTML5相比具有如下优势:富离线体验、消息推送、定时默认更行等功能,这些优势决定了HTML5无法取代native。service worker(后面简称sw)就是在这样的背景下提出来的。
sw是一段运行在浏览器后端的脚本,独立于页面,是一个worker,也可以理解为一个网络代理服务器。因此sw是无法与DOM进行交互的,但是可以与js主线程进行通信。
2. 实现的功能
目前sw还是一个草案,各个浏览器支持程度还不是很高,除了chrome 40、firefox以外,其他浏览器均不支持该功能,但是sw提供的逆天功能还是非常值得期待:
- 后台数据的同步
- 从其他域获取资源请求
- 接受计算密集型数据的更新,多页面共享该数据
- 客户端编译与依赖管理
- 后端服务的hook机制
- 根据URL模式,自定义模板
- 性能优化
- 消息推送
- 定时默认更新
- 地理围栏
其中最期待的还是通过HTTP请求的拦截,进而实现离线应用,提升页面的性能体验。
3. 简单使用
a. 首先在页面注册一个service worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./index.js').then((reg) => {
console.log('register a service worker: ', reg)
}).catch((err) => {
console.log('err: ', err);
});
}
b. 接着就可以在Service worker中尽情畅想
以页面的离线应用为出发点,说明sw如实做到离线应用的。
const cacheUrl = [
'/base.css',
'/france.html'
];
const cacheName = 'my-site-cache';
// install阶段
self.addEventListener('install', (event) => {
console.log('sw event: install');
event.waitUntil(
caches.open(cacheName).then((cache) => {
console.log('open cache');
return cache.addAll(cacheUrl);
})
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(caches.match(event.request).then(res => {
if (res) {
console.log('match');
return res;
}
return fetch(event.request);
}));
});
4. 生命周期
sw的说明周期主要包括三个阶段: install、active、working。下面这张图说明各个阶段完成的工作:
5. 事件机制
sw本质上也是一个worker,所以sw开发也是建立在事件的基础上,通过事件机制完成相关业务逻辑的处理。
其中sw里面的事件在原始事件对象EVENT,进行了拓展,例如fetch event里面拥有respondWith、waitUtil方法。
6. 补充介绍: Cache API
cache api就是对http的request/response进行缓存管理,是在service worker的规范中定义的,往往跟service worker一起操作使用,是实现web app离线应用的关键一环。但是cache api又不依赖于service worker,可以单独在window下使用,。
在window对象下,cache api的操作封装在caches
对象下面,里面的操作分为两类: 对cache的操作、对cache里面http的操作。下面简单说明下cache storage的相关操作:
// 下面是对cache的相关操作
// open: 创建或打开一个cache
caches.open('test').then(cache => {
return cache.add('/base.css')
}).then((val) => {
console.log('create a cache and add "base.css" to it');
});
// 在cache storage查找缓存的资源
caches.match('/base.css').then(res => {
if (!res) return 'can not find this http in caches storage';
return res.text()
}).then((result) => {
console.log(result)
});
// 得到所有的cache
caches.keys().then(name => {
console.log('names: ', name)
});
// 得到某个cache
caches.delete('test').then(val => {
console.log('delete success?: ', val)
});
// 对cache里面http进行操作
caches.open('test').then(cache => {
// 添加缓存资源
return cache.add('/base.css')
}).then((val) => {
console.log('create a cache and add "base.css" to it');
});
caches.open('test').then(cache => {
// 资源匹配
return cache.match('/base.css')
}).then(res => {
return res.text()
}).then(str => {
console.log(str)
});