使用workbox实现web离线缓存应用

2,488 阅读6分钟

workbox 是 GoogleChrome 团队推出的一套 Web App 静态资源本地存储的解决方案,该解决方案包含一些 Js 库和构建工具,在 Chrome Submit 2017 上首次隆重面世。而在 workbox 背后则是 Service WorkerCache API 等技术和标准在驱动。在 Workebox 之前,GoogleChrome 团队较早时间推出过 sw-precachesw-toolbox 库,但是在 GoogleChrome 工程师们看来,workbox 才是真正能方便统一的处理离线能力的更完美的方案,所以停止了对 sw-precache 和 sw-toolbox 的维护。

什么是workbox,workbox有什么用途,为什么要使用它?在介绍workbox之前,我们来先大致了解一下service worker,有助于我们后面更好地去理解workbox。

一. service worker 和 web worker

service worker是在浏览器后台独立于网页运行的脚本,它能够实现对网络请求进行缓存,并向网页推送和同步信息的功能,令人更加兴奋的是,它可以实现离线的情况下,也能看到我们的网页,极大提升了我们的用户体验。

service worker 已经得到越来越多的浏览器的支持,包括苹果、腾讯的X5内核。苹果从safari11开始,已经开始支持了。支持情况如下:

二. workbox的一些特性:

  • 不管你的站点是哪种方式构建的,都可以实现离线缓存的效果;
  • 自动管理好缓存列表,包括更新、同步、删除旧的缓存等;
  • 配置简单却不失灵活,可以完全自定义相关需求(支持 Service Worker 相关的特性如 Web Push, Background sync 等)。
  • 针对各种应用场景的多种缓存策略。

三. workbox的使用

下面来看下workbox的例子。

  1. 在入口页面的onload中,注册一个service worker,注册时引入缓存列表文件,也就是build.sw.js。

index.html

<script>
// Register A service worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register(`./sw.js`)
      .then(function(registration) {
        // Registration was successful
        console.log('[success] register ')
      }, function(err) {
        // registration failed :(
        console.log('[fail]: ', err);
      });
  });
 <script>
  1. 在sw.js页面配置缓存列表和缓存策略

    // 首先引入 Workbox 框架 importScripts('storage.googleapis.com/workbox-cdn…');

    // 注册成功后要立即缓存的资源列表 workbox.precaching.precacheAndRoute([ { "url": "css/index.css", "revision": "835ba5c3" }, { "url": "images/xxx.png", "revision": "b1537bfs" }, { "url": "index.html", "revision": "b331f695" }, { "url": "js/index.js", "revision": "4d562866" } ]);

    // 缓存策略, 网络优先 workbox.routing.registerRoute( new RegExp(''..html'), workbox.strategies.networkFirst() ); // 缓存优先 workbox.routing.registerRoute( new RegExp('..(?:js|css)'), workbox.strategies.cacheFirst() );

    // 请求: 缓存优先,同时后台更新后下次打开页面才会被页面使用 workbox.routing.registerRoute( new RegExp('https://your\.cdn\.com/'), workbox.strategies.staleWhileRevalidate() );

  • stateWhileRevalidate:当请求的路由有对应的 Cache 缓存结果就直接返回,在返回 Cache 缓存结果的同时会在后台发起网络请求拿到请求结果并更新 Cache 缓存,如果本来就没有 Cache 缓存的话,直接就发起网络请求并返回结果,这对用户来说是一种非常安全的策略
  • networkFirst:当请求路由是被匹配的,就采用网络优先的策略,也就是优先尝试拿到网络请求的返回结果,如果拿到网络请求的结果,就将结果返回给客户端并且写入 Cache 缓存,如果网络请求失败,那最后被缓存的 Cache 缓存结果就会被返回到客户端,这种策略一般适用于返回结果不太固定或对实时性有要求的请求,为网络请求失败进行兜底。
  • cacheFirst:当匹配到请求之后直接从 Cache 缓存中取得结果,如果 Cache 缓存中没有结果,那就会发起网络请求,拿到网络请求结果并将结果更新至 Cache 缓存,并将结果返回给客户端。这种策略比较适合结果不怎么变动且对实时性要求不高的请求
  • networkOnly:强制使用正常的网络请求,并将结果返回给客户端,这种策略比较适合对实时性要求非常高的请求。
  • cacheOnly:直接使用 Cache 缓存的结果,并将结果返回给客户端,这种策略比较适合一上线就不会变的静态资源请求。
    如果以上策略都不满足需求,还可以自定义策略

实现的效果如下

四. 使用workerbox后的效果

在我们的项目中,我们以DomContentLoaded的时间作为参考点,对比有加service worker 和未加的service worker情况。

测试条件

以首页为例,在不同的网络环境下,发起10次网络请求,然后取平均值,作为它们的最终结果,测试结果如下:

通过上面的数据可以得出几个结论:

  • 在弱环境下,service worker的优势越发明显,
  • 即使在wifi环境下面,由于存在缓存的情况,浏览器加载的速度也比未使用service worker的时间要短。
  • 在无网络环境的情况,也可以做到离线缓存的效果,极大地提升页面的用户体验。

五. 几个注意点

在经过一段时间的使用和思考以后,给出认为较为合理的缓存策略:

  • HTML,如果想让页面离线可以访问,使用 NetworkFirst,如果不需要离线访问,使用 NetworkOnly,其他策略均不建议对 HTML 使用。
  • CSS 和 JS,情况比较复杂,因为一般站点的 CSS,JS 都在 CDN 上,SW 并没有办法判断从 CDN 上请求下来的资源是否正确(HTTP 200),如果缓存了失败的结果,问题就大了。建议使用 Stale-While-Revalidate 策略,既保证了页面速度,即便失败,用户刷新一下就更新了。
  • 如果CSS,JS 与站点在同一个域下,并且文件名中带了 Hash 版本号,那可以直接使用 Cache First 策略。
  • 图片建议使用 Cache First,并设置一定的失效事件,请求一次就不会再变动了。

如果大家在使用过程中有更友好的策略,麻烦也贡献你们的策略,大家共同学习,共同进步。

还有,要牢记,对于不在同一域下的任何资源,绝对不能使用 Cache only 和 Cache first。

4.service worker的运行环境

需要注意的是,Service Worker 脚本除了域名为 localhost 时能运行在 http 协议下以外,只能运行 https 协议下。

5. 使用Service Worker缓存请求时,POST请求无法缓存

Google对web的标准化还是遵循的,SW认为POST请求就是象服务器提交资源,不存在缓存需求

  1. 注意资源是否和是同域

如果在同一域名,可以使用 workbox.strategies.cacheFirst() 进行缓存

不同域名绝对不能使用 Cache only 和 Cache first。可以使用 staleWhileRevalidate

  1. url 输入 chrome://inspect/#service-workers 可以调试和查看当前浏览器中使用service worker 的网页

8. 控制台 application 中可以查看当前页面是否运用了service worker

9 https sw 只工作在安全模式下

  • 本地的local和http 模式下sw都注册不成功。

10 策略

  • 静态资源图片等使用 Cache First
  • CSS 和 JS, 使用 Stale-While-Revalidate 策略,既保证了页面速度,即便失败,用户刷新一下就更新了
  • 不建议使用 Cache only 和 Cache first

demo 链接 github.com/lebornjose/…