Progressive Web Apps(PWA)核心技术-Cache API

Service Worker API带有一个Cache接口,可以让您创建按请求键入的响应存储。 缓存资源的常见模式有:service worker安装、用户交互、网络响应。

1、在service worker安装的时候缓存应用程序脚本
我们可以在这里缓存Html、css、js等一些静态资源。

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(cacheName).then(function(cache) {
      return cache.addAll(
        [
          '/css/bootstrap.css',
          '/css/main.css',
          '/js/bootstrap.min.js',
          '/js/jquery.min.js',
          '/offline.html'
        ]
      );
    })
  );
});

在第一次安装service worker的时候触发


注意:需要注意的是,当这个事件发生的时候,你的service worker的任何以前的版本仍然在运行和提供页面,所以你在这里做的事情不能破坏这个。 例如,这不是删除旧缓存的好地方,因为之前的service worker可能仍然在使用它们。

2.用户交互时

如果整个网站无法脱机,则可以让用户选择要脱机使用的内容(例如,视频,文章或照片库)。

一种方法是给用户一个“稍后阅读”或“保存离线”按钮。 点击时,从网络中获取所需内容并将其放入缓存中:

document.querySelector('.cache-article').addEventListener('click', function(event) {
  event.preventDefault();
  var id = this.dataset.articleId;
  caches.open('mysite-article-' + id).then(function(cache) {
    fetch('/get-article-urls?id=' + id).then(function(response) {
      // /get-article-urls returns a JSON-encoded array of
      // resource URLs that a given article depends on
      return response.json();
    }).then(function(urls) {
      cache.addAll(urls);
    });
  });
});

注意:缓存API在window对象上可用,这意味着您不需要service worker将内容添加到缓存中。


3.在网络响应时

如果请求与缓存中的任何内容都不匹配,则从网络获取该请求,将其发送到页面并同时将其添加到缓存中。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.open('mysite-dynamic').then(function(cache) {
      return cache.match(event.request).then(function (response) {
        return response || fetch(event.request).then(function(response) {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    })
  );
});

这种方法最适合频繁更新的资源,例如用户的收件箱或文章内容。 这对非必要的内容(例如头像)也是有用的,但需要小心。


注意:为了提高内存使用效率,您只能读取一次响应/请求的主体。 在上面的代码中,.clone()用于创建可单独读取的响应的副本。


从缓存中提取文件

要从缓存提供内容并使您的应用脱机,您需要拦截网络请求并使用缓存中存储的文件进行响应。 有几种方法:

  • cache only
  • network only
  • cache falling back to network
  • network falling back to cache
  • cache then network

仅从缓存中
你不需要经常专门处理这个文件。回退到网络的缓存通常是适当的方法。

这种方法适用于作为应用程序主代码(应用程序的“版本”的一部分)的任何静态资源。你应该在安装事件中缓存这些,所以你可以在那里依赖它们。

self.addEventListener('fetch',function(event){
  event.respondWith(caches.match(event.request));
});

如果在缓存中找不到匹配项,则响应看起来像连接错误。

仅从网络中
对于不能脱机执行的事情,这是正确的方法,例如分析ping和非GET请求。同样,你并不需要经常专门处理这个情况,并且先从缓存再到网络的方法通常会更合适。

self.addEventListener('fetch',function(event){
  event.respondWith(fetch(event.request));
});

或者,不要调用event.respondWith,因为这将导致默认的浏览器行为。

先缓存再回到网络
如果您先让应用程序离线,那么您将如何处理大部分请求。其他模式传入的请求将会异常。

self.addEventListener('fetch',function(event){
  event.respondWith(
    caches.match(event.request).then(function(response){
      return response||fetch(event.request);
    })
  );
});

这为缓存中的内容提供了“Cache only”行为,而对于任何未缓存的内容(包括所有非GET请求,因为它们不能被缓存),“Network only”行为都是如此。

先网络再回到缓存
对于经常更新的资源来说,这是一个很好的方法,而不是网站“版本”(例如,文章,头像,社交媒体时间表,游戏排行榜)的一部分。以这种方式处理网络请求意味着在线用户获得最新的内容,离线用户获得较旧的缓存版本。

但是,这种方法有缺陷。如果用户连接中断或连接速度较慢,则在从缓存中获取内容之前,必须等待网络请求失败。这可能需要很长时间,并且是令人沮丧的用户体验。查看下一个方法,缓存然后网络,以获得更好的解决方案。

self.addEventListener('fetch',function(event){
  event.respondWith(
    fetch(event.request).catch(function(){
      return caches.match(event.request);
    })
  );
});

在这里,我们首先使用fetch()将请求发送到网络,并且只有在失败的情况下才能在缓存中查找响应。

缓存然后网络
对于经常更新的资源来说,这也是一个很好的方法。这种方法将尽可能快地在屏幕上获得内容,但是一旦到达,仍然显示最新的内容。

这要求页面发出两个请求:一个到缓存,一个到网络。这个想法是首先显示缓存的数据,然后在/如果网络数据到达时更新页面。

这是页面中的代码:

var networkDataReceived = false;

startSpinner();

// fetch fresh data
var networkUpdate = fetch('/data.json').then(function(response) {
  return response.json();
}).then(function(data) {
  networkDataReceived = true;
  updatePage(data);
});

// fetch cached data
caches.match('/data.json').then(function(response) {
  if (!response) throw Error("No data");
  return response.json();
}).then(function(data) {
  // don't overwrite newer network data
  if (!networkDataReceived) {
    updatePage(data);
  }
}).catch(function() {
  // we didn't get cached data, the network is our last hope:
  return networkUpdate;
}).catch(showErrorMessage).then(stopSpinner());

我们同时向网络和缓存发送请求。缓存将最有可能首先响应,如果网络数据还没有收到,我们用缓存响应中的数据更新页面。当网络响应时,我们再次用最新的信息更新页面。

以下是service worker的代码:

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.open('mysite-dynamic').then(function(cache) {
      return fetch(event.request).then(function(response) {
        cache.put(event.request, response.clone());
        return response;
      });
    })
  );
});

获取到新数据的时候缓存网络响应数据。

有时您可能在新数据到达时替换当前数据(例如,游戏排行榜),但要小心,不要隐藏或替换用户可能与之交互的内容。例如,如果您从缓存中加载博客文章的页面,然后将新帖子添加到网页的顶部,那么您可以考虑调整滚动位置,以便用户不中断。如果您的应用程序布局非常线性,这可能是一个很好的解决方案

通用回退方法
如果您无法从缓存或网络提供某些内容,则可能需要提供一个通用回退方法。此技术适用于辅助图像,如头像,失败的POST请求,“离线时不可用”页面。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    // Try the cache
    caches.match(event.request).then(function(response) {
      // Fall back to network
      return response || fetch(event.request);
    }).catch(function() {
      // If both fail, show a generic fallback:
      return caches.match('/offline.html');
      // However, in reality you'd have many different
      // fallbacks, depending on URL & headers.
      // Eg, a fallback silhouette image for avatars.
    })
  );
});

您回退的项目很可能是安装依赖项。

您还可以根据网络错误提供不同的回退:

self.addEventListener('fetch', function(event) {
  event.respondWith(
    // Try the cache
    caches.match(event.request).then(function(response) {
      if (response) {
        return response;
      }
      return fetch(event.request).then(function(response) {
        if (response.status === 404) {
          return caches.match('pages/404.html');
        }
        return response
      });
    }).catch(function() {
      // If both fail, show a generic fallback:
      return caches.match('/offline.html');
    })
  );
});

网络响应错误不会在fetch promise中抛出错误。 相反,fetch返回包含网络错误的错误代码的响应对象。 这意味着我们在.then而不是.catch中处理网络错误。

删除过期的缓存

一旦安装了新的service worker并且以前的版本没有被使用,新的service worker就会激活,并且您将获得一个激活事件。 由于旧版本没有被使用,现在是删除未使用的缓存的好时机。

self.addEventListener('activate', function(event) {
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.filter(function(cacheName) {
          // Return true if you want to remove this cache,
          // but remember that caches are shared across
          // the whole origin
        }).map(function(cacheName) {
          return caches.delete(cacheName);
        })
      );
    })
  );
});

在激活期间,其他事件(如fetch)被放入队列中,所以长时间的激活可能会阻止页面加载。 保持激活尽可能精简,只用于旧版本激活时无法执行的操作。

参考链接:https://developers.google.com/web/ilt/pwa/caching-files-with-service-worker#using_the_cache_api

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

推荐阅读更多精彩内容