使用图像和 CSS 使后备页面脱机

Using images and CSS to offline fallback page

我正在尝试将我的网站设置为在没有互联网连接的情况下加载时有一个后备页面。为此,我正在关注 this guide on web.dev: "Create an offline fallback page"

我修改了文章中的示例 ServiceWorker 以适合我的目的,包括能够在后备离线页面中提供外部 CSS 和图像:

// Incrementing OFFLINE_VERSION will kick off the install event and force
// previously cached resources to be updated from the network.
const OFFLINE_VERSION = 1;
const CACHE_NAME = "offline";
// Customize this with a different URL if needed.
const OFFLINE_URL = "offline.html";

self.addEventListener("install", (event) => {
    event.waitUntil(
        (async () => {
            const cache = await caches.open(CACHE_NAME);
            // Setting {cache: 'reload'} in the new request will ensure that the response
            // isn't fulfilled from the HTTP cache; i.e., it will be from the network.
            await cache.add(new Request(OFFLINE_URL, { cache: "reload" }));
            await cache.add(new Request("offline.css", { cache: "reload" }));
            await cache.add(new Request("logo.png", { cache: "reload" }));
            await cache.add(new Request("unsupportedCloud.svg", { cache: "reload" }));
        })()
    );
});

self.addEventListener("activate", (event) => {
    // Tell the active service worker to take control of the page immediately.
    self.clients.claim();
});

self.addEventListener("fetch", (event) => {
    // We only want to call event.respondWith() if this is a navigation request
    // for an HTML page.
    if (event.request.mode === "navigate") {
        if (event.request.url.match(/SignOut/)) {
            return false;
        }
        event.respondWith(
            (async () => {
                try {
                    const networkResponse = await fetch(event.request);
                    return networkResponse;
                } catch (error) {
                    // catch is only triggered if an exception is thrown, which is likely
                    // due to a network error.
                    // If fetch() returns a valid HTTP response with a response code in
                    // the 4xx or 5xx range, the catch() will NOT be called.
                    console.log("Fetch failed; returning offline page instead.", error);

                    const cache = await caches.open(CACHE_NAME);
                    const cachedResponse = await cache.match(OFFLINE_URL);
                    return cachedResponse;
                }
            })()
        );
    }
});

但是,当 offline.html 页面加载时,它确实无法加载图像和 CSS;图像无法加载并出现 404 错误,并且 CSS 的请求甚至没有显示在浏览器开发控制台的“网络”选项卡中。

我希望从 ServiceWorker 缓存中获取图像和 CSS,但似乎两者都不是。

我是否遗漏了有关 ServiceWorkers 如何缓存请求或它们如何获取请求的信息?或者如何设计离线后备页面?

原来有几个原因导致找不到资产。

第一个原因是因为当它们被保存到缓存时,它们被保存在与 Service Worker 文件一起存储的整个路径中。

所以保存的路径与 static/PWA/[offline.css, logo.png, unsupportedCloud.svg] 类似,但请求它们的页面路径在根目录中。在 offline.html 中,我不得不这样引用它们:<img src="static/PWA/unsupportedCloud.svg" class="unsupported-cloud" />.

第二个原因 是 Service Worker 只检查 fetch 类型为导航的事件。在我的例子中你可以看到我写了 if (event.request.mode === "navigate") {...} 所以我们只尝试使用我们在导航事件中设置的缓存,这不会捕获 fetch 事件来获取资产。为了解决这个问题,我为 no-cors 事件模式设置了一个新的检查:else if (event.request.mode === "no-cors") {...}.

这两个修复让我可以从我在 Service Worker 安装时设置的离线缓存中获取资产。通过其他一些小修复,这解决了我的问题!