首页 > 解决方案 > Service worker: Cache by filename only not url

问题描述

I noticed that when you cache a file with a service worker

event.respondWith(
    caches.match(event.request)
        .then(function(response) {
            if (response) {
                return response;
            }
            return fetch(event.request);
        }
    )
);

enter image description here

the file is cached based on the url

/bundle/v1/assets/icon.svg 

But what I would like to have is only the file name on which the cache is base. Meaning that the same cache is used for

/bundle/v1/assets/icon.svg 
/bundle/v2/assets/icon.svg 
/bundle/v3/assets/icon.svg 

Is something like this possible?

标签: service-worker

解决方案


您可以做的是在读取和写入缓存存储 API 时获得一些创意,方法是在将 URL 用作键之前对其进行规范化。

使用您的示例,假设您知道每次与以 结尾的 URL 的 Cache Storage API(读取或写入)交互时icon.svg,您总是希望它引用相同的底层Response,而不管完整的 URL。您可以执行以下操作:

// A "fake" prefix used for all the normalized entries.
// Choose something that is not used by your real URLs.
const NORMALIZED_PREFIX = '/__normalized/';

// A list of "files" whose URLs you want to normalize.
const FILES_TO_NORMALIZE = [
  'icon.svg',
  // ...anything else...
];

function normalizeUrl(request) {
  // Convert the request.url string into a URL object,
  // so that we can get the pathname cleanly.
  const url = new URL(request.url);

  for (const file of FILES_TO_NORMALIZE) {
    if (pathname.endsWith(file)) {
      // If we have a match, then normalize the URL by using our
      // standard prefix along with the specific file name.
      url.pathname = NORMALIZED_PREFIX + file;
      return url.href;
    }

  }

  // Otherwise, just return the original request's URL.
  return request.url;
}

self.addEventListener('fetch', event => {
  // This is a naive cache-first strategy. Customize to suit your needs:
  // https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/
  event.respondWith(async function() {
    const requestKey = normalizeRequestUrl(event.request);

    const cache = await caches.open('runtime-cache');
    const cachedResponse = await caches.match(requestKey);

    // If there's a cached response, use it.
    if (cachedResponse) {
      return cachedResponse;
    }

    // Otherwise, get a response from the network for the original request.
    // Make sure you *don't* call fetch(requestKey),
    // since that URL doesn't exist on the server!
    const networkResponse = await fetch(event.request);

    // When you save the entry in the cache, use cache.put()
    // with requestKey as the first parameter.
    await cache.put(requestKey, networkResponse.clone());

    return networkResponse;
  }());
});

推荐阅读