将响应图像离线回退到最大的缓存图像

Offline fallback for responsive images to largest cached image

我有一个使用图像 srcset 的项目。它们都在自己的子目录中,类似于:

该站点在列表和详细视图中使用不同大小的图像。例如,从纵向移动到横向将从 100.jpg 变为 200.jpg,详细视图将使用 500 或 1000.jpg,具体取决于视口大小。

在离线情况下,是否有可能让工作箱查找使用以“/images/image1/”开头的 URI 缓存的内容,并找到编号最大的文件和 return?如果离线用户发生变化,我宁愿使用更小(或更大)的图像而不是损坏的图像。从纵向到横向。

好的!这是一个 requires Workbox v6, and makes use of the new handlerDidError lifecycle event 提供后备的自定义插件:

import {registerRoute} from 'workbox-routing';
import {CacheFirst} from 'workbox-strategies';

// Replace with your desired cache name.
const imagesCacheName = 'images-cache';

function parseImageNameAndSize(url) {
  const pattern = new RegExp('/([^/]+)/(\d+)\.jpg$');
  const [_, name, size] = pattern.exec(url) || [];

  // This will return [undefined, NaN] when there's no match.
  return [name, parseInt(size)];
}

async imageFallback({error, request}) {
  let largestSize = -1;
  let cacheKeyOfLargestImage;

  const [originalName, _] = parseImageNameAndSize(request.url);
  // If the URL doesn't match our RegExp, re-throw the underlying error.
  if (!originalName) {
    throw error;
  }

  const cache = await caches.open(imagesCacheName);
  const cachedRequests = await cache.keys();
  // Iterate through all of the cache entries to find matches:
  for (const cachedRequest of cachedRequests) {
    const [name, size] = parseImageNameAndSize(cachedRequest.url);
    if ((name === originalName) && (size > largestSize)) {
      largestSize = size;
      cacheKeyOfLargestImage = cachedRequest;
    }
  }

  if (cacheKeyOfLargestImage) {
    // If we have the cache key for the largest image that meets
    // the conditions, return the cached response.
    return cache.match(cacheKeyOfLargestImage);
  }

  // If there is no image the cache that satisfies these conditions,
  // re-throw the underlying error.
  throw error;
}

// Now, use the callback as a plugin by associating it with
// handerDidError in the strategy of your choice
// (CacheFirst, StaleWhileRevalidate, etc.):
registerRoute(
  // Match any request whose path ends in .jpg
  ({url}) => url.pathname.endsWith('.jpg'),
  new CacheFirst({
    cacheName: imagesCacheName,
    plugins: [
      {handlerDidError: imageFallback},
      // Add any other plugins you want.
    ],
  })
);

(我还没有测试所有这些代码,但我认为它应该接近工作。如果您 运行 遇到问题请告诉我!)

请注意,只有在给定策略无法满足 URL 的原始请求时,此插件才会“启动”,这很可能是因为您处于离线状态并且没有缓存匹配。如果您想配置 Workbox,使其始终在缓存中可用时使用最高质量的图像,即使您在线或缓存匹配较低的图像时也是如此-质量图像,可以这样做(可能在 cachedResponseWillBeUsed 回调中)。但我认为对于您描述的特定用例,使用新的 handlerDidError 回调是最好的方法。