美文网首页
使用Service Worker缓存文件

使用Service Worker缓存文件

作者: 魂斗驴 | 来源:发表于2021-03-29 09:12 被阅读0次

    Service Worker中使用缓存API

    Service Worker API带有 Cache接口,该接口可让您创建按请求键控的响应存储。尽管此接口是为service workers设计的,但实际上它是暴露在窗口中的,可以从脚本中的任何位置进行访问。入口点是caches

    您负责实现脚本(Service Worker)如何处理对缓存的更新。必须明确请求对缓存中所有项目的更新;项不会过期,必须删除。但是,如果缓存的数据量超过浏览器的存储限制,则浏览器将开始逐出与一个起点(一次一个起点)关联的所有数据,直到存储量再次低于该极限。有关更多信息,请参见 浏览器存储限制和限制条件

    储存资源

    在本节中,我们概述了一些用于缓存资源的常见模式: Service Worker安装用户交互网络响应

    安装时-缓存应用程序框架

    如果install启动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此时可能仍在使用它们。

    event.waitUntil延长install事件的生存期,直到传递的承诺成功解决为止。如果承诺被拒绝,则安装被视为失败,并且该服务工作程序被放弃(如果运行的是旧版本,它将保持活动状态)。

    cache.addAll如果任何资源无法缓存,将拒绝。这意味着仅当所有资源cache.addAll都已缓存时,Service Worker才会安装。

    关于用户交互

    如果无法使整个网站离线,则可以让用户选择他们希望离线使用的内容(例如,视频,文章或图片库)。

    一种方法是为用户提供“稍后阅读”或“保存为离线”按钮。单击它后,从网络中获取您所需的内容并将其放入缓存中:

    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);
        });
      });
    });
    

    在上面的示例中,当用户单击带有cache-article该类的元素时,我们将获取商品ID,获取具有该ID的商品,并将该商品添加到缓存中。

    注意: 缓存API在窗口对象上可用,这意味着您不需要让Service Worker将东西添加到缓存中。

    关于网络响应

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

    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;
            });
          });
        })
      );
    });
    

    这种方法最适合经常更新的资源,例如用户的收件箱或文章内容。这对于非必要内容(例如化身)也很有用,但需要小心。如果对一系列URL执行此操作,请注意不要膨胀原始存储-如果用户需要回收磁盘空间,则您不希望成为主要候选人。确保您摆脱了不再需要的缓存中的项目。

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

    从缓存中提取文件

    要提供缓存中的内容并使您的应用离线使用,您需要拦截网络请求并使用缓存中存储的文件进行响应。有几种方法可以解决此问题:

    • 仅缓存
    • 仅网络
    • 缓存回落到网络
    • 网络回退到缓存
    • 然后缓存网络

    仅缓存

    您不需要经常专门处理这种情况。 缓存回落到网络通常是适当的方法。

    这种方法适用于应用程序主代码(应用程序“版本”的一部分)中的任何静态资产。您应该已经将它们缓存在install事件中,因此您可以依靠它们。

    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);
        })
      );
    });
    

    这使您对缓存中的内容具有“仅缓存”行为,对于未缓存的任何事物(包括所有非GET请求,因为它们无法缓存),其为“仅网络”行为。

    网络回退到缓存

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

    但是,这种方法有缺陷。如果用户的连接是间歇性的或速度较慢,则他们必须等待网络出现故障,然后才能从缓存中获取内容。这可能会花费很长时间,并且是令人沮丧的用户体验。有关更好的解决方案,请参见下一种方法,即“先 缓存然后是网络”

    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承诺中引发错误。而是fetch返回包含网络错误的错误代码的响应对象。这意味着我们用a.then代替a处理网络错误.catch

    删除过时的缓存

    一旦安装了新的Service Worker并且未使用以前的版本,则将激活新的Service Worker,并且您会收到一个activate事件。由于旧版本已无法使用,因此现在是删除未使用的缓存的好时机。

    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放入队列中),因此长时间激活可能会阻止页面加载。保持激活尽可能精简,仅将其用于旧版本处于活动状态时无法执行的操作。

    使用缓存API

    在这里,我们介绍了Cache API的属性和方法。

    检查支持

    我们可以检查浏览器是否支持Cache API,如下所示:

    if ('caches' in window) {
      // has support
    }
    

    创建缓存

    一个源可以有多个命名的Cache对象。要创建缓存或打开与现有缓存的连接,我们使用的 caches.open方法。

    caches.open(cacheName)
    

    这将返回一个承诺,该承诺将解析为缓存对象。caches.open接受将是缓存名称的字符串。

    处理数据

    缓存API带有多种方法,可让我们在缓存中创建和操作数据。这些可以分为创建,匹配或删除数据的方法。

    建立资料

    我们可以使用三种方法将数据添加到缓存。这些 addaddAllput。实际上,我们将在从返回的缓存对象上调用这些方法caches.open()。例如:

    caches.open('example-cache').then(function(cache) {
            cache.add('/example-file.html');
    });
    

    Caches.open返回example-cacheCache对象,该对象将传递到中的回调.then。我们add在此对象上调用方法以将文件添加到该缓存。

    cache.add(request)-add方法获取URL,对其进行检索,然后将所得的响应对象添加到给定的缓存中。该对象的关键是请求,因此我们稍后可以通过该请求再次检索该响应对象。

    cache.addAll(requests)-此方法与add相同,除了它采用URL数组并将其添加到缓存中。如果无法将任何文件添加到缓存中,则整个操作将失败,并且不会添加任何文件。

    cache.put(request, response)-此方法同时接收请求和响应对象,并将它们添加到缓存中。这使您可以手动插入响应对象。通常,您只需要fetch()一个或多个请求,然后将结果直接添加到缓存中。在这种情况下,最好cache.add还是使用或cache.addAll,因为它们是以下一项或多项操作的简写功能:

    fetch(url).then(function (response) {
      return cache.put(url, response);
    })
    

    匹配数据

    有两种方法可以在缓存中搜索特定内容: matchmatchAll。可以在caches对象上调用这些,以搜索所有现有的缓存,也可以在从返回的特定缓存上进行搜索caches.open()

    caches.match(request, options)-此方法返回一个Promise,该Promise解析为与一个或多个缓存中的第一个匹配请求关联的响应对象。undefined如果找不到匹配项,则返回。第一个参数是请求,第二个参数是用于优化搜索的可选选项列表。这是MDN定义的选项:

    • ignoreSearch:一个布尔值,指定是否忽略URL中的查询字符串。例如,如果设置为true?value=bar一部分,则http://foo.com/?value=bar在执行匹配时将被忽略。默认为false
    • ignoreMethod:一个布尔值,当设置true为时,阻止匹配操作验证Request HTTP方法(通常仅允许GET和HEAD。)它默认为false。
    • ignoreVary:一个布尔值,当设置true为时告诉匹配操作不要执行VARY标头匹配-也就是说,如果URL匹配,则无论Response对象是否具有VARY标头,您都将获得一个匹配项。默认为false
    • cacheName:一个DOMString,表示要在其中进行搜索的特定缓存。请注意,此选项被忽略Cache.match()

    caches.matchAll(request, options)-此方法与.match其他方法相同,只是它从缓存中返回所有匹配的响应,而不仅仅是第一个。例如,如果您的应用程序缓存了图像文件夹中包含的某些图像,我们可以返回所有图像并对它们执行一些操作,如下所示:

    caches.open('example-cache').then(function(cache) {
      cache.matchAll('/images/').then(function(response) {
        response.forEach(function(element, index, array) {
          cache.delete(element);
        });
      });
    })
    

    删除资料

    我们可以使用删除缓存中的项目cache.delete(request, options)。此方法在缓存中找到与请求匹配的项目,将其删除,然后返回解析为的Promise true。如果找不到该项目,则将其解析为false。它还具有与match方法相同的可选options参数。

    检索密钥

    最后,我们可以使用获取缓存键列表cache.keys(request, options)。这将返回一个Promise,该Promise解析为一组缓存键。这些将以插入缓存中的相同顺序返回。这两个参数都是可选的。如果未传递任何内容,则cache.keys返回缓存中的所有请求。如果传递了请求,它将从缓存中返回所有匹配的请求。这些选项与先前方法中的选项相同。

    还可以在缓存入口点上调用keys方法,以返回缓存本身的密钥。这使您可以一次性清除过时的缓存。

    参考

    Caching Files with Service Worker

    相关文章

      网友评论

          本文标题:使用Service Worker缓存文件

          本文链接:https://www.haomeiwen.com/subject/dmnscltx.html