service-worker虽然已列入标准,但是支持的浏览器还是有限制,还有比较多的问题。
1. 生命周期
注册成功-------installing--------------> 安装成功(installed)(waiting) ---------activating----------> 激活成功 (activated)------> 销毁(redundant)
其中任何一个步骤失败都将进入销毁(redundant)
a. 注册:
调用 navigator.serviceWorker.register 方法,第一个参数是 service-worker对应的js文件, 第二个参数可选,scope是表示 service-worker 的作用域,最大的作用域就是 service-worker.js所在目录。
这2个参数都相对于origin目录,而不是项目根目录。
比如 https://mdn.github.io/sw-test/sw.js ,项目根目录是
https://mdn.github.io/sw-test/,路径
应该写成
/sw-test/sw.js 而非
/sw.js
.
不写scope默认就是最大的作用域,{scope: '/service-worker/foo/'} 表示service-worker只在foo目录下生效,一定要保证当前sw是最新的且没有被之前sw控制的页面,否则没效果。
navigator.serviceWorker.('/service-worker/service-worker.js', {scope: '/service-worker/foo/'}) .then(function (registration) { console.log('registration:',registration); }) .catch(function (e) { console.error(e); })
b. 安装
注册成功后进入installing状态,这时会触发 install 事件, 在这个事件里面我们可以添加文件到缓存,如果不调用 skipWaiting 当前sw会在安装成功后进入waiting状态,直到所有页面都没有被旧的sw控制为止。
调用skipWaiting 将使当前sw直接进入 activating 状态。在开发调试时最好设置skipWaiting ,否则刷新页面新的sw一直处于waiting而不被使用。
self.addEventListener('install', function (event) { // 监听worker的install事件 self.skipWaiting();//让新 SW 立即激活。这里不会跳过 installing, 只会等安装成功后跳过 waiting直接将之前存在的sw销毁,新的sw进入到activating event.waitUntil( // 延迟install事件直到缓存初始化完成 caches.open(CACHE_VERSION) .then(function (cache) { console.log('Opened cache'); return cache.addAll(CACHE_FILES); // 会从网络加载需要缓存的文件,这时当前sw的fetch还没监听 }) ); });
3. 激活
sw从waiting状态进入到 activating 这时会触发 activate 事件, 可以在这个事件里面处理缓存的清理功能,这里要注意 self.clients.claim ,假如页面没有sw控制,这个方法可以让当前页面马上sw控制,在控制之后的请求都会被fetch监听到,如果没有调用这个方法就必须刷新页面或关了页面重新进来页面才会被sw控制。
self.addEventListener('activate', function (event) { // 监听worker的activate事件 event.waitUntil( // 延迟activate事件直 到 caches.keys().then(function(keys){ return Promise.all(keys.map(function(key, i){ // 清除旧版本缓存 if(key !== CACHE_VERSION){ //console.log(keys, key) return caches.delete(keys[i]); } })) }) ) //When a service worker is initially registered, pages won't use it until they next load. The claim() method causes those pages to be controlled immediately. self.clients.claim(); //让没被控制的 clients(页面、workers) 受控, 否则要刷新页面才受控,比如资源加载触发fetch(可以设置延时加载模拟) });
激活成功后进入activated状态,这时fetch、message、put事件被触发。
self.addEventListener('fetch', function(event) { // Do stuff with fetch events }); self.addEventListener('message', function(event) { // Do stuff with postMessages received from document });
可以利用fetch事件监听资源请求,进行修改响应或请求,发现请求资源没在缓存列表,可以从网络加载资源,并添加到缓存列表
self.addEventListener('fetch', function (event) { // 截取页面的资源请求 console.log('fetching',event.request); //网络请求触发该事件 event.respondWith( caches.match(event.request).then(function (response) { if(res){ // 匹配缓存返回缓存中的资源 return res; }else{ throw new Error(); } }).catch(function() { //没有在缓存列表,重新请求网络 return fetch(event.request).then(function(response) { //if not a valid response send the error if(!response || response.status !== 200 || response.type !== 'basic'){ return response; } return caches.open(CACHE_VERSION).then(function(cache) { cache.put(event.request, response.clone()); //请求的资源添加到缓存 return response; //返回请求的响应 }); }); }).catch(function() { //请求失败返回默认的资源 return caches.match('/sw-test/gallery/myLittleVader.jpg'); }) ); });
service worker 所涉及的知识比较多,包括cache缓存、promise等。
至于cache能缓存多大的容量,同一域名下的 ServiceWorkerCache 也只能使用 40M,参考:https://zhuanlan.zhihu.com/p/27586862
PWA的核心技术包括:
Web App Manifest – 在主屏幕添加app图标,定义手机标题栏颜色之类
Service Worker – 缓存,离线开发
App Shell – 先显示APP的主结构,再填充主数据,更快显示更好体验
Push Notification – 消息推送
可以参考百度的基于vue的pwa脚手架:https://lavas.baidu.com/
参考文档:
https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers
http://www.zhangxinxu.com/wordpress/2017/07/service-worker-cachestorage-offline-develop/
https://segmentfault.com/a/1190000006061528
https://segmentfault.com/a/1190000007487049#articleHeader2
https://zhuanlan.zhihu.com/p/20040372