具有多个缓存的 Service Worker

Service Worker with Multiple Caches

有许多示例可用于使用类似于以下内容的单个缓存来初始化 Service Worker:

let cacheName = 'myCacheName-v1';
let urlsToCache = ['url1', 'url2', url3'];

self.addEventListener('install', function (event) {
  event.waitUntil(
    caches.open(cacheName).then(function (cache) {
      return cache.addAll(urlsToCache);
    }).then(function () {
      return this.skipWaiting();
    })
  );
});

我希望在我的 service worker 上初始化多个缓存。动机是根据资产的变化趋势对资产进行分组(例如,静态应用程序数据与 css、javascript 等)。使用多个缓存,我可以更新单个缓存(通过版本化的缓存名称)作为缓存中的文件更改。理想情况下,我希望设置类似于以下的结构:

let appCaches = [{
    name: 'core-00001',
    urls: [
      './',
      './index.html', etc...
    ]
  },
  {
    name: 'data-00001',
    urls: [
      './data1.json',
      './data2.json', etc...
    ]
  },
  etc...
];

到目前为止我最好的尝试是类似于:

self.addEventListener('install', function (event) {
  appCaches.forEach(function (appCache) {
    event.waitUntil(
      caches.open(appCache.name).then(function (cache) {
        return cache.addAll(appCache.urls);
      }));
  });
  self.skipWaiting();
});

这种方法似乎有效。但是,我仍然是 service worker 和 promises 的新手。有些东西告诉我,这种方法有一个陷阱,我太缺乏经验而无法识别。有没有更好的方法来实现这个?

最好只在一个处理程序中调用 event.waitUntil 一次,但好处是它相对容易等待一个 Promise。

类似的东西应该可以工作:

event.waitUntil(Promise.all(
  myCaches.map(function (myCache) {
    return caches.open(myCache.name).then(function (cache) {
      return cache.addAll(myCache.urls);
    })
  )
));

Promise.all 采用 Promise 数组并仅在数组中的所有 promise 解析后解析,这意味着 install 处理程序将等待所有缓存初始化。

感谢 pirxpilot! Your answer, combined with JavaScript Promises: an Introduction,经过大量试验和错误找出如何将所有这些 Promise 链接在一起的结果是一个不错的小实现。

我添加了仅缓存更改的要求。根据最佳实践,旧缓存会在 'activate' 事件期间被删除。最终产品是:

let cacheNames = appCaches.map((cache) => cache.name);

self.addEventListener('install', function (event) {
  event.waitUntil(caches.keys().then(function (keys) {
    return Promise.all(appCaches.map(function (appCache) {
      if (keys.indexOf(appCache.name) === -1) {
        return caches.open(appCache.name).then(function (cache) {
          console.log(`caching ${appCache.name}`);
          return cache.addAll(appCache.urls);
        })
      } else {
        console.log(`found ${appCache.name}`);
        return Promise.resolve(true);
      }
    })).then(function () {
      return this.skipWaiting();
    });
  }));
});

self.addEventListener('activate', function (event) {
  event.waitUntil(
    caches.keys().then(function (keys) {
      return Promise.all(keys.map(function (key) {
        if (cacheNames.indexOf(key) === -1) {
          console.log(`deleting ${key}`);
          return caches.delete(key);
        }
      }));
    })
  );
});