Service Worker

Service Worker是什么


从功能上来说,Service Worker是一种提供离线缓存控制功能的一种Worker,同时,也具有消息推送和后台同步的功能,可以通过Service Worker来缓存网页的资源,然后拦截Fetch请求来执行相应的缓存处理操作。因为是一种Worker,所以Service Worker也具有Worker的一些基本特性,例如:

  • 独立于主线程运行
  • 不能访问window对象,但是拥有自身的一个执行上下文,例如Service WorkerServiceWorkerGlobalScope
  • 具有消息api来和页面进行消息交互

Service Worker是一种共享型Worker,它不同于专用型Worker只能在创建它的页面中使用,默认配置下,service Worker可以被当前注册脚本的域名下所有页面公用。
也就是说,只要在一个根域名注册一个ServiceWorker,那么所有这个域名下的页面都会收到影响。

Service Worker使用


要使用ServiceWorker,首先,需要通过serviceWorkerContainer.register()来进行注册,例如:

ServiceWorkerContainer.register("/test/service.js", {scope:"./"})
    .then(
        function(ServiceWorkerRegistration) {
            // do something
        }
);

假设当前域名为www.baidu.com/serviceWork,那么上面的方法就在www.baidu.com/serviceWork/test下面注册了一个ServiceWorker,假如把scope改成./hahaha,那么作用域就变成了www.baidu.com/serviceWork/test/hahaha,只要处于这个域名之下的所有页面,都受到这个ServiceWorker的控制。但是,假如将scope改成"../",页面就会报出一个错误,因为这时候不允许设置比service.js所在位置层级更高的路径,除非添加一个Service-Worker-Allowedheader。

注册完成之后,受控界面会去安装serviceWorker,安装完成之后会处于等待状态,接下来有可能会进入激活状态,激活状态之后,页面还不一定是受控的,不过有相应的api可以控制这一系列流程。这些流程就是ServiceWorker最最复杂的生命周期了。

ServiceWorker生命周期


serviceWorker的生命周期有点复杂,情况很多,但是基本上符合一个理念,那就是渐进式。

install

执行完注册之后,浏览器会去下载,假如脚本没有错误的话,就会进行安装,安装会在Worker内触发install事件,安装分为两个状态:installinginstalled,这里可以执行一些操作。

self.addEventListener("install",installEvent=>{
    self.skipWaiting();//跳过waiting
    installEvent.waitUntil(
        caches.open(CACHE_NAME).then(cache=>{
            return cache.add("http://upload.jianshu.io/users/upload_avatars/9545112/f186bd4a-1da4-4912-b926-a744bc128d06.png?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240");
        })
    )

});

这里会有一个installEvent,通过waitUtil方法可以阻塞安装状态,这个方法可以传入一个promise,只有这个promise正确resolve之后,才会完成安装,同时,还有一个skipwaiting方法,这个方法可以跳过installed和activating状态之间的waiting状态。
首次打开页面会安装work,然后接下来再检测到新的work也会再次执行安装。
触发更新的几种情况:

  • 第一次导航到作用域范围内页面的时候
  • 当在24小时内没有进行更新检测并且触发功能性时间如push或sync的时候
  • SW 的 URL 发生变化并调用.register()时
  • 手动执行reg.update()
  • 重载页面(有些情况下不会更新,原因不明)

更新会比对旧版本和新版本的字节,假如字节不一致,则更新。在更新install完成之后,会进入waiting状态,直到旧版本不控制任何client(受控的页面)再进入activating状态。

activate

activate的状态比测试妹子的思考逻辑还难猜测。但是这个遵循一个原则,只有所有的页面都不受老的serviceWorker控制的时候,才会开始激活。意思就是所有worker作用的页面都关了,相当于重启更新,再次启动的时候,才会进入激活状态。激活也会触发一个activate状态.

self.addEventListener("activate", ExtendableEvent => {
  ExtendableEvent.waitUntil(self.clients.claim());
 }

activate也和install一样,也有waitUtil方法,效果也是一样的。激活完成之后其实还不一定有效果,因为页面可能还处于不受控的状态,或者有另外一个页面,没有刷新的,但是这个时候新开一个页面更新了work,这个时候就需要调用clients.claim操作来让所有打开的页面受控。但是...work的执行是异步的,也就是说,页面在执行这个方法之前是不受控,或者是不是受最新的work控制的,在利用work来缓存的时候,就会出现问题,导致版本不一致,所以,个人感觉serviceWorker比较适合做一个辅助者,没有也行,有也行,可以通过设置一个button之类的来提醒用户,发现新版本,是否需要刷新之类的操作。
下面这张图很好的概括了生命周期

生命周期

Fetch

激活完成之后,work内可以拦截到fetch事件,通过这个,就可以执行缓存操作。fetch事件回调会返回一个fetchEvent对象,通过fetchEvent.respondWith()可以自定义返回的response。

其他事件

....

杂谈


ServiceWorker的大多数功能都要在worker线程中使用,在页面中,通过注册返回的ServiceWorkerRegistration可以获取很多有用的信息,例如正在安装的worker,等待中的worker,激活的worker,worker作用域等。同时,可以监听状态的变换。例如,

ServiceWorkerRegistration.addEventListener("updatefound",()=>{
            const netWorker=ServiceWorkerRegistration.installing;
            console.log(`state:${netWorker.state}`);
            netWorker.addEventListener("statechange", () => {
              // newWorker 状态发生变化
              console.log(`state变换:${netWorker.state}`)
              if(netWorker.state=="installed"){
                console.log("检测到新版本!");
              }
            });
        })

这里,先获取正在安装中的worker,然后监听状态变换,当切换成installed状态时,假如之前已经存在serviceWorker,就可以判定新安装的worker是更新进去的,这个时候就有可能部分资源是通过老的worker缓存请求的,部分资源在worker受控之后是通过worker缓存请求的,这样就存在不一致的问题,所以这时候就可以提醒用户有新版本更新,从而刷新更新。假如之前没有worker,那么就说明这个worker是第一次注册安装,因为是第一次,所以没有所谓的缓存资源版本不一致的问题,没必要提醒用户更新。
针对serviceWorker机制所导致的各种资源同步问题,将其想像成渐进式的就轻松多了,将worker当作一个辅助者,把缓存想象成可有可无的。进一步来看可以不使用skipWaiting,只有在用户重启的时候才可以更新,这时候所有的资源就不存在不同步的问题了,所有的请求都会在重启更新之后直接进入worker。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,711评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,079评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,194评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,089评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,197评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,306评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,338评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,119评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,541评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,846评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,014评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,694评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,322评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,026评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,257评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,863评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,895评论 2 351

推荐阅读更多精彩内容