PWA笔记二:离线缓存原理

ServiceWorker既然命名为worker,很大一部分原因就是它和WebWorker相关。它是在第二个线程完成缓存代理的任务,不会影响dom渲染的主线程,两个Worker之间的通讯是基于postMessage,两个线程是不能直接进行通讯。

这一点和小程序有点像,但是又不一样。 小程序是把渲染层和逻辑层用两个线程进行分离。逻辑层的报错,并不会影响渲染层的展示,具体这么做能够提升多少优化量,微信团队并没有给出一个很确切的数据统计。PWA使用worker更多是为了处理离线缓存的内容,并且会使用indexedDB来存缓存文件的版本编号,UI的渲染层和逻辑层(可以被称之为主线层)并没有得到分离。

前提条件

基于HTTPS

HTTPS 不仅仅可以保证你网页的安全性,还可以让一些比较敏感的 API 完美的使用。值得一提的是,SW 是基于 HTTPS 的,所以,如果你的网站不是 HTTPS,那么基本上你也别想了 SW。

Scope作用域

一个sw.js并不能接管一个站点所有的页面,它只能在所在路由底下起到作用。意思就是如果你在//example.com/foo/bar.js里注册了一个 SW,那么它默认的作用域为//example.com/foo/

SPA

虽然说PWA可以使用在多页面应用(MPA)上,一个sw文件和一个manifest文件主要的设计理念在于搭配着用在SPA,SW在SPA使用更合理。

生命周期

注册

ServiceWorker.js(又名sw.js)是一个独立js,页面注册在浏览器支持的情况下,注册sw.js来控制Service Worker缓存。register将会触发安装声明周期,所有的源码都是有原生浏览器实现。

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js').then(function(registration) {
    console.log('ServiceWorker registration successful with scope: ',    registration.scope);
  }).catch(function(err) {
    console.log('ServiceWorker registration failed: ', err);
  });
}

install

注册完成后会出发安装的生命周期,把设置好的静态文件,采用Service Worker的缓存方式,使用了Cache API来将资源缓存起来,同时使用e.waitUntil接手一个Promise来等待资源缓存成功,等到这个Promise状态成功后,ServiceWorker进入installed状态,意味着安装完毕。这时候主线程中返回的registration.waiting属性代表进入installed状态的ServiceWorker。

var CACHE_NAME = "my_cache";
var urlsToCache = [
  '/index.html',
  '/css/style.css',
  '/js/script.js'
];
//这里的self代表ServiceWorkerGlobalScope
self.addEventListener('install', function(event) {
//这里的waitUtil会在安装成功之前执行一些预装的操作,但是只建议做一些轻量级和非常重要资源的缓存,减少安装失败的概率。安装成功
//后ServiceWorker状态会从installing变为installed 
event.waitUntil(
        caches.open(CACHE_NAME).then(function(cache) {
             console.log('Opendhe : ',cache);
            return cache.addAll(urlsToCache);
      })
    );
});

skipWaiting

skipWaiting()意味着新 SW 控制了之前用旧 SW 获取的页面,也就是说你的页面有一部分资源是通过旧 SW 获取,剩下一部分是通过新 SW 获取.

activate

安装完,则会进入激活状态。如果之前已有ServiceWorker,这个版本只是对ServiceWorker进行了更新。如果你在event.waitUntil()中传入了一个 Promise,SW 将会缓存住功能性事件(fetch,push,sync等等),直到 Promise 返回 resolve 的时候再触发,也就是说,当你的fetch事件被触发的时候,SW 已经被完全激活了。

self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(keys => Promise.all(
      keys.map(key => {
        if (!expectedCaches.includes(key)) {
          return caches.delete(key);
        }
      })
    )).then(() => {
      // V2控制缓存
    })
  );
});

fetch

fetch请求是有别于xhr请求,sw提供监听拦截fetch的事件,对于命中缓存的数据可以直接返回请求。当接受到 fetch 请求时,会直接返回event.respondWith 得到Promise 结果。这样我们可以捕获页面所有的 fetch 请求。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }
        return fetch(event.request);
      }
    )
  );
});

redundant

Service Worker 可能以下之一的原因而被废弃(redundant,原意为“多余的,累赘的”)——

  • installing 事件失败
  • activating 事件失败
  • 新的 Service Worker 替换其成为激活态 worker

调试方法

方法一

chrome://inspect/#service-workers 就可以查看当前浏览器正在注册的 SW,并且可以对它们进行调试和结束进程。调试会直接在service worker线程进行,不用理会主线层的逻辑。

调试Service worker

方法二

另外,还有 chrome://serviceworker-internals,用来查看当前浏览器中所有注册过的 SW。输入这个地址就像打开新世界的大门,原来你访问那么多PWA页面。

所有注册过的service worker

方法三

在打开chrome的调试面板devtools,Application tab里面有个service workers页面,可以针对该页面的SW,进行缓存调试以及消息推送,在这里也可以看到SW所在的生命周期,大大提高SW的调试效率。

chrome查看缓存情况

更新问题

浏览器获取了新版本的ServiceWorker代码,如果浏览器本身对sw.js进行缓存的话,也不会得到最新代码,所有代码会变成死代码无法更新。这里有两种解决方案:

  1. 在ngnix或cdn的缓存配置中,sw文件最好配置成cache-control: no-cache
  2. 采用sw-register-webpack-plugin来处理sw文件的更新问题。

参考

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