【问题标题】:Workbox redirect the clients page when resource is not cached and offline当资源未缓存且脱机时,Workbox 重定向客户端页面
【发布时间】:2019-10-07 20:24:39
【问题描述】:

通常,每当我阅读有关 PWA 的博客文章时,该教程似乎只是预先缓存了每一个资产。但这似乎有点违背应用程序外壳模式,据我了解,这是:缓存基本必需品(仅应用程序外壳),并在运行时缓存。 (如果我理解不正确,请纠正我)

想象一下,我有这个单页应用程序,它是一个带有 Web 组件的简单 index.html:<my-app><my-app> 组件设置了一些看起来有点像这样的路由,我正在使用 Vaadin 路由器和 Web 组件,但我想使用 React 和 React Router 或类似的东西会出现同样的问题。

router.setRoutes([
    {
        path: '/',
        component: 'app-main', // statically loaded
    },
    {
    path: '/posts',
        component: 'app-posts',
        action: () => { import('./app-posts.js');} // dynamically loaded
    },
    /* many, many, many more routes */
    {
        path: '/offline', // redirect here when a resource is not cached and failed to get from network
        component: 'app-offline', // also statically loaded
    }
]);

我的应用程序可能有很多很多路线,并且可能会变得非常大。我不想立即预缓存所有这些资源,而只缓存我绝对需要的东西,所以在这种情况下:我的index.htmlmy-app.jsapp-main.jsapp-offline.js。我想在运行时缓存app-posts.js,当它被请求时。

设置运行时缓存很简单,但是当我的用户访问可能许多尚未缓存的路由之一时,我的问题就出现了(因为可能用户之前没有访问过该路由,所以js 文件可能尚未加载/缓存),并且用户没有互联网连接。

在这种情况下(当路由尚未缓存且没有网络时)我想要发生的事情是将用户重定向到由我的客户端路由器处理的/offline 路由。我可以轻松地执行以下操作:import('./app-posts.js').catch(() => /* redirect user to /offline */),但我想知道是否有办法从工作箱本身实现这一目标。

简而言之: 当js文件还没有被缓存,用户没有网络,文件请求失败时:让workbox将页面重定向到/offline路由。

【问题讨论】:

  • 我已经看到了,但是我仍然必须返回一个响应,对吧?我想做的是将页面从我的服务人员重定向到/offline。或者有没有办法做到这一点?
  • 我只为图像设置了 catch 处理程序,但是第二个链接中的代码示例返回了一个后备 html url,我没有尝试过,但它看起来可以工作。
  • 但这并没有真正解决它,我的问题是:当对这个 javascript 文件的请求失败时,我可以从服务人员那里以某种方式让客户端转到 /offline 路由吗?我想我可以返回一个(预缓存的)404.js,它只是导航到/offline 路线,但它看起来很笨拙。我想知道是否有更好/更好的方法来处理这个
  • 你看到这个问题了吗? stackoverflow.com/questions/53503761/…

标签: service-worker progressive-web-apps workbox


【解决方案1】:

选项 1(并不总是有用): 据我所知,根据this answer,您无法从服务人员中打开新窗口或更改浏览器的 URL。但是,只有在clients.openWindow() 函数notificationclick 事件内部调用时,您才能打开一个新窗口。

Option 2(最难): 您可以在 service worker 的 activate 事件中使用 WindowClient.navigate 方法,但是有点棘手,因为您仍然需要检查请求的文件是否存在于缓存中。

选项 3(最简单和最简单): 否则,您可以使用新的Request 对象响应离线页面:

const cacheOnly = new workbox.strategies.CacheOnly();
const networkFirst = new workbox.strategies.NetworkFirst();

workbox.routing.registerRoute(
  /\/posts.|\/articles/, 
  async args => {
    const offlineRequest = new Request('/offline.html');

    try {
      const response = await networkFirst.handle(args);
      return response || await cacheOnly.handle({request: offlineRequest});

    } catch (error) {
      return await cacheOnly.handle({request: offlineRequest})

    }
  }
);

然后在你的offline.html文件中重写浏览器的URL:

 <head>
    <script>
        window.history.replaceState({}, 'You are offline', '/offline');
    </script>
 </head>

选项3中的上述逻辑将首先使用网络响应请求的URL。如果网络不可用,将回退到缓存,即使在缓存中找不到请求,也会取而代之的是 offline.html 文件。一旦offline.html文件被解析,浏览器URL将被替换为/offline

【讨论】:

  • 这看起来很有希望!当我尝试这个时,我得到了一个The FetchEvent for "http://localhost:1117/app-posts-7f1dba0f.js" resulted in a network error response: an object that was not a Response was passed to respondWith(). 错误,我尝试了几件事,但我似乎无法让它工作......有什么想法吗? (附注:感谢拼写更正:))
  • 我最初使用clients.openWindow 是错误的,因为它只能在某些情况下使用。我已经用更多选项更新了答案。
猜你喜欢
  • 2016-05-06
  • 2019-01-19
  • 2011-02-02
  • 2010-09-19
  • 1970-01-01
  • 1970-01-01
  • 2012-06-03
  • 2014-04-09
  • 1970-01-01
相关资源
最近更新 更多