如何从缓存中加载不同的文件?

How to load different files from cache?

我正在使用 Service Worker 提供显示用户离线的后备页面。 Service Worker 在拦截请求期间获取相同的请求,并在获取错误时从缓存中为 'offline.html' 请求提供响应。这样做的一个小片段是。

self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then(() => {
      return fetch(event.request).catch((err) => {
        return caches.match("offline.html");
      });
    })
  );
});

现在,如果离线 html 有其他请求,可能是对其 css 文件或图像的请求,我该如何从缓存中加载它们。我试过执行以下操作:

self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then(() => {
      return fetch(event.request).catch((err) => {
        let url = event.request.url;
        if(url.endsWith('.css')) return caches.match('offline.css');
        if(url.endsWith('.jpg') || url.endsWith('.png')) return caches.match('images/banner.jpg');
        return caches.match("offline.html");
      });
    })
  );
});

但是有更好的方法吗?有这样做的标准方法吗?

首先,我建议您在决定是否使用 offline.html 作为后备内容之前检查是否 event.request.destination === 'document'。这确保您不会意外返回 HTML 文档来满足,比如说,碰巧失败的随机 API 请求。

此外,您当前的代码包括 caches.match(event.request) 但实际上并没有使用缓存的响应,这可能不是您想要的。

也就是说,让我们来看看我认为是您想要的逻辑:

  • 您的服务工作者尝试对网络发出请求。
  • 如果该请求 returns 有效响应,使用它,您就完成了。
  • 如果该请求失败,则:
    • 如果是导航请求,无论目的地如何 URL,请使用缓存的 offline.html 作为响应。
    • 否则,对于非导航请求(如 CSS 或 JS 请求),使用匹配所需 URL 的缓存条目作为响应。

这是实现它的服务工作者。您需要确保 CSS、JS 和 offline.html 资产是 cached during service worker installation;这只包括 fetch 处理程序逻辑。

self.addEventListener('install', (event) => {
  event.waitUntil(
    /* Cache your offline.html and the CSS and JS it uses here. */
  );
});

async function fetchLogic(request) {
  try {
    // If the network request succeeds, just use
    // that as the response.
    return await fetch(request); 
  } catch(error) {
    // Otherwise, implement fallback logic.
    if (request.mode === 'navigate') {
      // Use the cached fallback.html for failed navigations.
      return await caches.match('offline.html');
    }
    // Otherwise, return a cached copy of the actual
    // subresource that was requested.
    // If there's a cache miss for that given URL, you'll
    // end up with a NetworkError, just like you would if
    // there were no service worker involvement.
    return await caches.match(request.url);
  }
}

self.addEventListener('fetch', (event) => {
  event.respondWith(fetchLogic(event.request));
});

this article 中也有一些正式的指导。