Service Worker 将 API 调用中的文件添加到预缓存

Service worker add files from API call to precache

启用我的应用 运行 离线。在安装过程中,service worker 应该:

  1. 从异步中获取 URL 列表 API
  2. 重新格式化响应
  3. 在对预缓存的响应中添加所有 URL

为此,我将 Googles Workbox 与 Webpack 结合使用。

问题:虽然 service worker 成功缓存了所有 Webpack 资产(这告诉我工作箱基本上做了它应该做的),但它没有等待异步 API 调用来缓存额外的远程资产。它们会被简单地忽略,既不会缓存也不会在网络中获取。

这是我的服务工作者代码:

importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.1.0/workbox-sw.js');

workbox.skipWaiting();
workbox.clientsClaim();
    
self.addEventListener('install', (event) => {
  const precacheController = new 
  workbox.precaching.PrecacheController();
  const preInstallUrl = 'https://www.myapiurl/Assets';
  event.waitUntil(fetch(preInstallUrl)
    .then(response => response.json()
    .then((Assets) => {
      Object.keys(Assets.data.Assets).forEach((key) => {
        precacheController.addToCacheList([Assets.data.Assets[key]]);
      });
    })
  );
});

self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
    
workbox.routing.registerRoute(/^.*\.(jpg|JPG|gif|GIF|png|PNG|eot|woff(2)?|ttf|svg)$/, workbox.strategies.cacheFirst({ cacheName: 'image-cache', plugins: [new workbox.cacheableResponse.Plugin({ statuses: [0, 200] }), new workbox.expiration.Plugin({ maxEntries: 600 })] }), 'GET');

这是我的工作箱 webpack 配置:

new InjectManifest({
  swDest: 'sw.js',
  swSrc: './src/sw.js',
  globPatterns: ['dist/*.{js,png,html,css,gif,GIF,PNG,JPG,jpeg,woff,woff2,ttf,svg,eot}'],
  maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
})

我意识到我的错误。我希望这也能帮助其他人。问题是我没有手动调用 precacheController.install() 。虽然此函数将自动执行,但它不会等待异步插入的其他预缓存文件。这就是为什么需要在所有预缓存发生后调用该函数的原因。这是工作代码:

importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.1.0/workbox-sw.js');

workbox.skipWaiting();
workbox.clientsClaim();

const precacheController = new workbox.precaching.PrecacheController();

// Hook into install event
self.addEventListener('install', (event) => {
  // Get API URL passed as query parameter to service worker
  const preInstallUrl = new URL(location).searchParams.get('preInstallUrl');

  // Fetch precaching URLs and attach them to the cache list
  const assetsLoaded = fetch(preInstallUrl)
    .then(response => response.json())
    .then((values) => {
      Object.keys(values.data.Assets).forEach((key) => {
        precacheController.addToCacheList([values.data.Assets[key]]);
      });
    })
    .then(() => {
      // After all assets are added install them
      precacheController.install();
    });
  event.waitUntil(assetsLoaded);
});

self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});

workbox.routing.registerRoute(/^.*\.(jpg|JPG|gif|GIF|png|PNG|eot|woff(2)?|ttf|svg)$/, workbox.strategies.cacheFirst({ cacheName: 'image-cache', plugins: [new workbox.cacheableResponse.Plugin({ statuses: [0, 200] }), new workbox.expiration.Plugin({ maxEntries: 600 })] }), 'GET');

看起来您正在创建自己的 PrecacheController 实例并使用 precacheAndRoute(),它们实际上并不打算一起使用(在文档中没有很好地解释,它是仅在 this one place).

中提及

问题是 workbox.precaching.* 上的辅助方法实际上在幕后创建了它们自己的 PrecacheController 实例。由于您正在创建自己的 PrecacheController 实例并且 调用 workbox.precaching.precacheAndRoute([...]),因此您最终会得到两个无法正常工作的 PrecacheController 实例在一起。

从您的代码示例来看,您似乎正在创建一个 PrecacheController 实例,因为您想要加载文件列表以在运行时进行预缓存。没关系,但是如果您要这样做,需要注意以下几点:

  • 您的软件可能不会更新 Service Worker 更新通常在您调用 navigator.serviceWorker.register() 并且浏览器检测到 Service Worker 文件已更改时触发。这意味着如果您更改 /Assets returns 但您的 service worker 文件的内容没有改变,您的 service worker 将不会更新。这就是为什么大多数人在他们的 service worker 中硬编码他们的预缓存列表(因为对这些文件的任何更改都会触发新的 service worker 安装)。

  • 您必须手动添加自己的路线 我之前提到 workbox.precaching.precacheAndRoute([...]) 在后台创建自己的 PrecacheController 实例。它还adds its own fetch listener manually to respond to requests. That means if you're not using precacheAndRoute(), you'll have to create your own router and define your own routes. Here are the docs on how to create routes: https://developers.google.com/web/tools/workbox/modules/workbox-routing.