【问题标题】:Workbox offline fallback for XHR RequestsXHR 请求的 Workbox 离线回退
【发布时间】:2020-12-21 19:18:15
【问题描述】:

我正在使用 NextJS 和 Workbox 来创建 PWA 和我需要的这个库的离线支持:https://github.com/shadowwalker/next-pwa。在上面的 repo 中有一个我需要的示例:离线后备。我不需要应用程序在离线模式下完全运行,只需要一个指示连接丢失的后备页面。

我阅读了关于综合后备的工作箱部分:https://developers.google.com/web/tools/workbox/guides/advanced-recipes#comprehensive_fallbacks

当任何其他路由无法生成响应时会触发一个 catchHandler,但问题是我在捕获 XMLHttpRequests (XHR) 错误时遇到了很大的麻烦。

例如,当客户端将请求发送到 API 时,如果没有互联网连接,我想呈现一个备用页面。如果失败的请求是“文档”,则处理程序仅处理后备页面,并且由于 XHR 请求不是文档,我无法处理它们。

import { clientsClaim } from 'workbox-core';
import { ExpirationPlugin } from 'workbox-expiration';
import {
  NetworkOnly,
  NetworkFirst,
  StaleWhileRevalidate,
} from 'workbox-strategies';
import {
  registerRoute,
  setDefaultHandler,
  setCatchHandler,
} from 'workbox-routing';
import {
  precacheAndRoute,
  cleanupOutdatedCaches,
  matchPrecache,
} from 'workbox-precaching';

clientsClaim();

// must include following lines when using inject manifest module from workbox
// https://developers.google.com/web/tools/workbox/guides/precache-files/workbox-build#add_an_injection_point
const WB_MANIFEST = self.__WB_MANIFEST;
// Precache fallback route and image
WB_MANIFEST.push({
  url: '/fallback',
  revision: '1234567890',
});

cleanupOutdatedCaches();

precacheAndRoute(WB_MANIFEST);

registerRoute(
  '/',
  new NetworkFirst({
    cacheName: 'start-url',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 1,
        maxAgeSeconds: 86400,
        purgeOnQuotaError: !0,
      }),
    ],
  }),
  'GET'
);

// disable image cache, so we could observe the placeholder image when offline
registerRoute(
  /\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,
  new NetworkOnly({
    cacheName: 'static-image-assets',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 64,
        maxAgeSeconds: 86400,
        purgeOnQuotaError: !0,
      }),
    ],
  }),
  'GET'
);

registerRoute(
  /\.(?:js)$/i,
  new StaleWhileRevalidate({
    cacheName: 'static-js-assets',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 32,
        maxAgeSeconds: 86400,
        purgeOnQuotaError: !0,
      }),
    ],
  }),
  'GET'
);

registerRoute(
  /\.(?:css|less)$/i,
  new StaleWhileRevalidate({
    cacheName: 'static-style-assets',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 32,
        maxAgeSeconds: 86400,
        purgeOnQuotaError: !0,
      }),
    ],
  }),
  'GET'
);

registerRoute(
  /\.(?:json|xml|csv)$/i,
  new NetworkFirst({
    cacheName: 'static-data-assets',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 32,
        maxAgeSeconds: 86400,
        purgeOnQuotaError: !0,
      }),
    ],
  }),
  'GET'
);

registerRoute(
  /https:\/\/api[a-z-]*\.pling\.net\.br.*$/i,
  new NetworkFirst({
    cacheName: 'pling-api',
    networkTimeoutSeconds: 10,
    plugins: [
      new ExpirationPlugin({
        maxEntries: 16,
        maxAgeSeconds: 86400,
        purgeOnQuotaError: !0,
      }),
    ],
  }),
  'GET'
);

registerRoute(
  /https:\/\/[a-zA-Z0-9]+\.cloudfront.net\/.*$/i,
  new NetworkFirst({
    cacheName: 'cloudfront-assets',
    networkTimeoutSeconds: 10,
    plugins: [
      new ExpirationPlugin({
        maxEntries: 32,
        maxAgeSeconds: 86400,
        purgeOnQuotaError: !0,
      }),
    ],
  }),
  'GET'
);

registerRoute(
  /.*/i,
  new NetworkFirst({
    cacheName: 'others',
    networkTimeoutSeconds: 10,
    plugins: [
      new ExpirationPlugin({
        maxEntries: 32,
        maxAgeSeconds: 86400,
        purgeOnQuotaError: !0,
      }),
    ],
  }),
  'GET'
);

setDefaultHandler(new NetworkOnly());

// This "catch" handler is triggered when any of the other routes fail to
// generate a response.
setCatchHandler(({ event }) => {
  switch (event.request.destination) {
    case 'document':
      // If using precached URLs:
      return matchPrecache('/fallback');
    case 'image':
      return matchPrecache('/static/images/fallback.png');
    default:
      // If we don't have a fallback, just return an error response.
      // Switch statement for XHR Requests  
      return Response.error();
  }
});

【问题讨论】:

    标签: next.js progressive-web-apps workbox


    【解决方案1】:

    您描述的场景——源自已加载页面的失败 XHR 应该触发“错误页面”——可能最好通过 window 上下文中的客户端代码解决,而不是通过服务工作者逻辑。我认为这更符合 Service Worker 的“意图”使用方式,并且会带来更好的用户体验。

    执行此操作的代码如下所示;

    const xhrRequest = new XMLHttpRequest();
    // Set request details and make the request.
    
    xhrRequest.addEventListener('error', (event) => {
      // Do something to display a "Sorry, an error occurred."
      // message within your open page.
    });
    

    因此,与其在 XHR 失败时尝试加载完全不同的页面,不如在现有页面的某处显示错误消息。 (如何显示此消息的详细信息取决于您通常如何处理页面的 UI。)

    如果您真的想在 XHR 失败时将当前页面完全替换为专用的错误页面,那么在 error 事件侦听器中,您可以执行 window.location.href = '/offline.html'

    如果你真的真的想为此使用服务工作者(出于某种原因;我认为你不应该),理论上你可以使用@ 987654321@ 在基于 Workbox 的服务人员中强制导航:

    setCatchHandler(async ({ event }) => {
      switch (event.request.destination) {
        case 'document':
          // If using precached URLs:
          return matchPrecache('/fallback');
        case 'image':
          return matchPrecache('/static/images/fallback.png');
        default:
          if (event.request.url === 'https://example.com/api') {
            // See https://developer.mozilla.org/en-US/docs/Web/API/WindowClient/navigate
            const client = await self.clients.get(event.clientId);
            await client.navigate('/offline.html');
          }
    
          // If we don't have a fallback, just return an error response.
          // Switch statement for XHR Requests  
          return Response.error();
      }
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-18
      • 2020-10-28
      • 1970-01-01
      • 2019-01-03
      • 1970-01-01
      • 1970-01-01
      • 2022-01-13
      • 2020-03-24
      相关资源
      最近更新 更多