PWA – Workbox缓存机制

Workbox是Google开发的一个工具库,主要用于实现PWA的本地文件管理,也就是一种缓存管理机制。

PWA一项重要功能是缓存,它能让站点利用缓存文件实现快速加载甚至是离线工作。如果没有Workbox,那开发者可能需要自己编写缓存管理的逻辑方法。提高效率的事情,何乐而不为,所以咱们就看看Workbox到底是什么鬼。在此之前,可以回顾一下ServiceWorker的使用PWA应用实战step2-Service Worker

使用方法

结合构建工具

Workbox可以结合构建工具来使用,比如:
– webpack
– gulp

CLI

Workbox 提供一款CLI工具,使用更方便。

$ npm install workbox-cli --global

# 使用默认设置创建一个 service worker
$ workbox generate:sw

引用类库直接编写ServiceWorker

在service-worker.js直接编写逻辑会更简明直接。因此Workbox提供了另一个工具,workbox-sw

$ npm install --save workbox-sw

安装后在ServiceWorker文件中引入workbox-sw类库,
然后直接编写逻辑JS代码即可。下面示例是将文件(标注版本)缓存下来。

importScripts('/node_modules/workbox-sw/build/workbox-sw.vX.X.X.prod.js');

const workboxSW = new WorkboxSW();
workboxSW.precache([
  {
    url: '/index.html',
    revision: 'bb121c',
  }, {
    url: '/styles/main.css',
    revision: 'acd123',
  }, {
    url: '/scripts/main.js',
    revision: 'a32caa',
  }
]);

缓存策略解读

const workboxSW = new WorkboxSW();
const networkFirst = workboxSW.strategies.networkFirst();
workboxSW.router.registerRoute('/schedule', networkFirst);

以上代码是对路由”/schedule”应用了一套策略,
通过调用workboxSW.strategies的方法对象,实现策略的应用。
strategies的对象有
– cacheOnly
– cacheFirst
– staleWhileRevalidate
– networkOnly
– networkFirst

对应缓存策略有以下几种:
* Cache only:仅使用缓存。页面通过ServiceWorker获取缓存,如果请求的内容缓存中没有,那么直接得到的结果是请求失败或者页面无法访问。
* Cache first, falling back to network:缓存优先,网络支援。就是在1的基础上,如果缓存中没有再向远程资源发起网络请求。

  • Stale While Revalidate:动态回源,如果有可用的缓存版本,则使用该版本,但下次会获取更新。

  • Network only:仅使用网络,这个很容易理解,通过ServiceWorker访问网络。注意,这跟直接请求网络并不完全一样。

  • Network first, falling back to cache:网络优先,缓存支援。在网络请求失败的基础上再去缓存里查找,如果有则使用缓存数据。这种方式并不能发挥缓存的最大优势,通常也只有在网络访问失败时才访问缓存,而网络时快时慢,用户多半是在网络请求中度过的。

以上是workbox-sw.Strategies提供的方法,

此外,再介绍一种方式:
* Cache, with network update:缓存和网络竞态(这个是google的描述),如何理解竞态呢,大概就是从网络获取资源比访问硬盘更快的情况下,网络资源优先,反之使用磁盘资源。
实现静态的思路,利用了Promise对象,将多个promise对象选择最先返回的结果用作最终结果。
以下代码进行详细说明:

function promiseRace(promises) {
    return new Promise((resolve, reject) => {
        // 对每个promise对象进行遍历,取得优先返回resolve结果
        promises.forEach(p => p.then(resolve));

        // 如果得到reject,需要判断所有请求均reject
        promises.reduce((a, b) => a.catch(() => b))
          .catch(() => reject(Error("All failed")));
    });
};

// 发起一个请求,包含从cache中得到结果和fetch请求解决的操作
self.addEventListener('fetch', function(event) {
   event.respondWith(
        promiseRace([
            caches.match(event.request),
            fetch(event.request)
        ])
    );
});

以上代码实现了竞态的判断。除此外Promise.race()是实现竞态的一种直接的方法,但Promise.race有两个缺点,一是reject结果返回则立即得到reject,不会等到所有promise的结果都被触碰。另一个方面是Promise.race兼容性还存疑。所以推荐上面的做法。

参考:

stale-while-revalidate
workbox

发表评论

电子邮件地址不会被公开。 必填项已用*标注