使用 Service Workers 来缓存应用数据

选择一个正确的缓存策略是很重要的,并且这取决于你应用中使用的数据的类型。比如像天气信息、股票信息等对实时性要求较高的数据,应该时常被刷新,但是用户的头像或者文字内容应该以较低的频率进行更新。

先使用缓存后使用请求结果 的策略对于我们的应用是非常理想的选择。应用从缓存中获取数据,并立刻显示在屏幕上,然后在网络请求返回后再更新页面。如果使用 先请求网络后缓存 的策略,用户可能不会等到数据从网络上加载回来便离开了应用。

先使用缓存后使用请求结果 意味着我们需要发起两个异步的请求,一个从请求缓存,另一个请求网络。我们应用中的网络请求不需要进行修改,但我们需要修改一下 service worker 的代码来缓存网络请求的响应并返回响应内容。

通常情况下,应该立刻返回缓存的数据,提供应用能够使用的最新信息。然后当网络请求返回后应用应该使用最新加载的数据来更新。

截获网络请求并缓存响应结果

我么需要修改 service worker 来截获对天气 API 的请求,然后缓存请求的结果,以便于以后使用。先使用缓存后使用请求结果 的策略中,我们希望请求的响应是真实的数据源,并始终提供给我们最新的数据。如果它不能做到,那也没什么,因为我们已经从缓存中给应用提供了最新的数据。

在 service worker 中,我们添加一个 dataCacheName 变量,以至于我们能够将应用数据和应用外壳资源分开。当应用外壳更新了,应用外壳的缓存就没用了,但是应用的数据不会受影响,并时刻保持能用。记住,如果将来你的数据格式改变了,你需要一种能够让应用外壳和应用数据能后保持同步的方法。

将下面代码添加至你的 service-worker.js 中:

 
var dataCacheName = 'weatherData-v1';

接下来,我么需要更新activate事件的回调函数,以它清理应用程序的外壳(app shell)缓存,并不会删除数据缓存。

 
if (key !== cacheName && key !== dataCacheName) {

最后,我么需要修改 fetch 事件的回调函数,添加一些代码来将请求数据 API 的请求和其他请求区分开来。

 
self.addEventListener('fetch', function(e) {  
  console.log('[ServiceWorker] Fetch', e.request.url);  
  var dataUrl = 'https://publicdata-weather.firebaseio.com/';  
  if (e.request.url.indexOf(dataUrl) === 0) {  
    // Put data handler code here  
  } else {  
    e.respondWith(  
      caches.match(e.request).then(function(response) {  
        return response || fetch(e.request);  
      })  
    );  
  }  
});

这段代码对请求进行拦截,判断请求的 URL 的开头是否为该天气 API,如果是,我们使用 fetch 来发起请求。一旦有响应返回,我们的代码就打开缓存并将响应存入缓存,然后将响应返回给原请求。

接下来,使用下面代码替换 // Put data handler code here

 
e.respondWith(  
  fetch(e.request)  
    .then(function(response) {  
      return caches.open(dataCacheName).then(function(cache) {  
        cache.put(e.request.url, response.clone());  
        console.log('[ServiceWorker] Fetched&Cached Data');  
        return response;  
      });  
    })  
);

我们的应用目前还不能离线工作。我们已经实现了从缓存中返回应用外壳,但即使我们缓存了数据,依旧需要依赖网络。

发起请求

之前提到过,应用需要发起两个异步请求,一个从请求缓存,另一个请求网络。应用需要使用 window 上的caches 对象,并从中取到最新的数据。这是一个关于渐进增强 极佳 的例子,因为 caches 对象可能并不是在任何浏览器上都存在的,且就算它不存在,网络请求依旧能够工作,只是没有使用缓存而已。

为了实现该功能,我们需要:

  1. 检查 cahces 对象是否存在在全局 window 对象上。
  2. 向缓存发起请求

  3. 如果向服务器发起的请求还没有返回结果,使用缓存中返回的数据更新应用。

  4. 向服务器发起请求

  5. 保存响应结果便于在之后使用

  6. 使用从服务器上返回的最新数据更新应用
从缓存中获取资料

接下来,我们需要检查 caches 对象是否存在,若存在,就向它请求最新的数据。将下面这段代码添加至app.getForecast() 方法中。

 
if ('caches' in window) {
  /*
   * Check if the service worker has already cached this city's weather
   * data. If the service worker has the data, then display the cached
   * data while the app fetches the latest data.
   */
  caches.match(url).then(function(response) {
    if (response) {
      response.json().then(function updateFromCache(json) {
        var results = json.query.results;
        results.key = key;
        results.label = label;
        results.created = json.query.created;
        app.updateForecastCard(results);
      });
    }
  });
}

我们的天气应用现在发起了两个异步请求,一个从缓存中,另一个经由 XHR。如果有数据存在于缓存中,它将会很快地(几十毫秒)被返回并更新显示天气的卡片,通常这个时候 XHR 的请求还没有返回来。之后当 XHR 的请求响应了以后,显示天气的卡片将会使用直接从天气 API 中请求的最新数据来更新。

如果因为某些原因,XHR 的响应快于 cache 的响应,hasRequestPending 标志位会阻止缓存中数据覆盖从网路上请求的数据。

 
var cardLastUpdatedElem = card.querySelector('.card-last-updated');
var cardLastUpdated = cardLastUpdatedElem.textContent;
if (cardLastUpdated) {
  cardLastUpdated = new Date(cardLastUpdated);
  // Bail if the card has more recent data then the data
  if (dataLastUpdated.getTime() < cardLastUpdated.getTime()) {
    return;
  }
}

亲自尝试

现在应用应该能够离线工作了。尝试关闭里本地启动的服务器,并切断网络,然后刷新页面。

然后去DevTools的 Application 面板上的 Cache Storage 窗格。 展开该部分,你应该会在左边看到您的app shell缓存的名称。当你点击你的appshell缓存,你将会看到所有已经被缓存的资源。

 

转载于:https://www.cnblogs.com/wonderhow2/p/6294889.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值