在介绍service之前,先引入一下PWA(由Google的Chrome团队创造),技术支持主要是ServiceWorker和Web App Mainfest
PWA是一种webapp模型,是一种渐进的web app,利用一些web特性,配合ui使用,增强web app的用户体验。(手机使用Safari 进入 m.weibo.cn/beta 将它保存在手机桌面上,点击桌面上相应快捷方式时,不会再显示url)。
那么刚才已经说过了,PWA有一个特性 -- 浏览器离线和弱网络环境可以急速访问。 这就使用了即将介绍的Service Worker:
功能:
它是充当在web应用程序与浏览器之间的代理服务器。离线缓存
特性:
后台消息传递
网络代理、转发请求、伪造响应
离线缓存
消息推送
现阶段来看,serviceWorker的功能主要集中在网络资源代理和离线缓存上。可以理解为他是一个在浏览器离线时,仍能运行的web wokrer。因此,window以及dom都是不能访问的。可以利用self访问全局上下文。
service worker 完全异步、同步API (XHR、LocalStroage)不能在其中使用。
正常情况下他是需要https环境才能跑起来的。但是本地服务器(127.0.0.1 / localhost)是可以使用的。
打开终端
重启apache:sudo /usr/sbin/apachectl restart
关闭apache:sudo /usr/sbin/apachectl stop
开启apache:sudo /usr/sbin/apachectl start
他的生命周期如下:共包含四个状态:安装、激活、等待、销毁(解雇)
在缓存的时候。我主要使用了cache ,cache和cacheStroage都是Service Worker API下的接口, 那么他们的区别是什么呢?
cacheStroage 主要保存了每个service worker 声明的cache对象。
举个例子理解一下:cacheStroage 是一个管理着所有cache的库
cache 是单个缓存库
(可以直接使用全局的caches属性访问cacheStroage)
cacheStroage主要有open 、match、has、delete、keys五个核心方法。可以对cache的不同匹配进行不同响应。注意:上面这五种方法都返回一个promise对象。
跑题了...
接下来说一下 service worker 的使用
首先。可以在控制台 - > application -> service worker 进行一些对于service worker 的操作(update,delete等)
https://lavas-project.github.io/pwa-book/chapter04/4-service-worker-debug.html
下面讲一下它的具体使用:
// 基本的项目结构如下
界面如下:
// 首先在serviceworker.html 文件中对他进行注册和 一些请求的发送(为了后面截取请求)
if (navigator.serviceWorker != null) {
// 使用浏览器特定方法注册一个新的service worker 返回值是promise对象
navigator.serviceWorker.register('sw.js')
.then(function(registration) {
window.registration = registration;
console.log('Registered events at scope: ', registration.scope);
});
}
fetch("./data.json")
fetch("./offline.json").then((res)=>{
console.log("offlinetest");
})
var statusEl = document.querySelector("#network-status");
// js监测在线离线状态
if(!navigator.onLine){
// 返回元素类名
statusEl.classList = ["is-offline"];
statusEl.innerText = 'offline';
}
// 具体sw.js(service worker)文件如下:
里面写了一些日志信息。
共分4部分
part 1:
1. 设置cache关键字(唯一标识)
2. cachelist 需要缓存的资源
console.log("service Test");
var cacheStorageKey = 'minimal-pwa-3';
var cacheList = [
'/',
"serviceworker.html",
"index.css",
"serviceworker.png",
"data.json"
]
part 2:开始执行生命周期
1. sw内部触发 install事件
2. 打开一个缓存空间 将list 添加进去
3. 如果所有文件都缓存成功,则service worker 将被安装。如果任何文件无法下载和缓存,则安装步骤将失败。service worker 将不会激活(即不会被安装)
// 当浏览器解析完sw文件时,serviceworker内部触发install事件
// 仅触发一次
self.addEventListener('install', function(e) {
// debugger;
console.log('Cache event!')
// 打开一个缓存空间,将相关需要缓存的资源添加到缓存里面
e.waitUntil(
caches.open(cacheStorageKey).then(function(cache) {
console.log('Adding to Cache:', cacheList)
return cache.addAll(cacheList)
}).then(function() {
console.log('install event open cache ' + cacheStorageKey);
console.log('Skip waiting!')
return self.skipWaiting();
})
)
})
part 3:
1. sw内部触发 active事件
2. 通常进行一些过期资源释放的处理
// 如果当前浏览器没有激活的service worker或者已经激活的worker被解雇
self.addEventListener('activate', function(e) {
debugger;
console.log('Activate event');
console.log('Promise all', Promise, Promise.all);
// active事件中通常做一些过期资源释放的工作
var cacheDeletePromises = caches.keys().then(cacheNames => {
console.log('cacheNames', cacheNames, cacheNames.map);
return Promise.all(cacheNames.map(name => {
if (name !== cacheStorageKey) { // 如果资源的key与当前需要缓存的key不同则释放资源
console.log('caches.delete', caches.delete);
var deletePromise = caches.delete(name);
console.log('cache delete result: ', deletePromise);
return deletePromise;
} else {
return Promise.resolve();
}
}));
});
console.log('cacheDeletePromises: ', cacheDeletePromises);
e.waitUntil(
Promise.all([cacheDeletePromises]
).then(() => {
console.log('activate event ' + cacheStorageKey);
console.log('Clients claims.')
return self.clients.claim();
})
)
})
part 4:
1. 请求的拦截 、转发 、资源缓存等
这里主要体现了资源缓存的部分:(可以考虑动态将资源缓存下来--利用cache.put)
// 拦截网络请求
self.addEventListener('fetch', function(e) {
console.log('Fetch event ' + cacheStorageKey + ' :', e.request.url);
e.respondWith( // 首先判断缓存当中是否已有相同资源
caches.match(e.request).then(function(response) {
if (response != null) { // 如果缓存中已有资源则直接使用
// 否则使用fetch API请求新的资源
console.log('Using cache for:', e.request.url)
return response
}
console.log('Fallback to fetch:', e.request.url)
return fetch(e.request.url)
// 动态缓存部分
})
)
})