在 Service Worker 中检测离线状态的最佳实践

Posted

技术标签:

【中文标题】在 Service Worker 中检测离线状态的最佳实践【英文标题】:Best practices for detecting offline state in a service worker 【发布时间】:2018-02-12 16:23:03 【问题描述】:

我有一个服务工作者,它应该缓存在客户端没有网络连接时显示的offline.html 页面。但是,它有时会认为导航器处于离线状态,即使它不在。即navigator.onLine === false。这意味着即使在线,用户也可能会收到offline.html 而不是实际内容,这显然是我想避免的。

这就是我在main.js 中注册服务人员的方式:

// Install service worker for offline use and caching
if ('serviceWorker' in navigator) 
  navigator.serviceWorker.register('/service-worker.js', scope: '/');

我现在的service-worker.js

const OFFLINE_URL = '/mysite/offline';
const CACHE_NAME = 'mysite-static-v1';

self.addEventListener('install', (event) => 
  event.waitUntil(
    // Cache the offline page when installing the service worker
    fetch(OFFLINE_URL,  credentials: 'include' ).then(response =>
      caches.open(CACHE_NAME).then(cache => cache.put(OFFLINE_URL, response)),
    ),
  );
);

self.addEventListener('fetch', (event) => 
  const requestURL = new URL(event.request.url);

  if (requestURL.origin === location.origin) 
    // Load static assets from cache if network is down
    if (/\.(css|js|woff|woff2|ttf|eot|svg)$/.test(requestURL.pathname)) 
      event.respondWith(
        caches.open(CACHE_NAME).then(cache =>
          caches.match(event.request).then((result) => 
            if (navigator.onLine === false) 
              // We are offline so return the cached version immediately, null or not.
              return result;
            
            // We are online so let's run the request to make sure our content
            // is up-to-date.
            return fetch(event.request).then((response) => 
              // Save the result to cache for later use.
              cache.put(event.request, response.clone());
              return response;
            );
          ),
        ),
      );
      return;
    
  

  if (event.request.mode === 'navigate' && navigator.onLine === false) 
    // Uh-oh, we navigated to a page while offline. Let's show our default page.
    event.respondWith(caches.match(OFFLINE_URL));
    return;
  

  // Passthrough for everything else
  event.respondWith(fetch(event.request));
);

我做错了什么?

【问题讨论】:

你试过玩ononline和onoffline吗?可能会帮助您找到来源...但是根据我们随处可见的情况,我建议您使用其他机制来了解您是否应该提供离线版本。原因之一是,当您的用户在线时,这并不意味着您的服务器可以访问/在线。 它是否与 -> ***.com/questions/14283124/… 有关,如果是这样,它看起来是查看是否在线的最佳方法,是一个后台任务,间歇性 ajax 调用或类似,也许更好的选择可能甚至是您服务器的 websocket,因此您可以比 navigator.onLine 更信任它.. @Keith,可能是相关的,尽管它在那个问题中是相反的。我会检查ajax请求是否解决了问题。 【参考方案1】:

navigator.onLine 和相关事件在您想要更新 UI 以指示您处于离线状态时非常有用,例如,仅显示缓存中存在的内容。

但我会避免编写依赖于检查navigator.onLine 的服务工作者逻辑。相反,尝试无条件地发出fetch(),如果失败,则提供备份响应。这将确保您的 Web 应用程序按预期运行,无论 fetch() 是否由于离线、lie-fi 或您的 Web 服务器遇到问题而失败。

// Other fetch handler code...

if (event.request.mode === 'navigate') 
  return event.respondWith(
    fetch(event.request).catch(() => caches.match(OFFLINE_URL))
  );


// Other fetch handler code...

【讨论】:

这是一个很好的方法,但似乎也会在错误 403 时触发离线页面,从而扰乱我们的登录过程。 不应该。只要返回某种响应,即使响应具有非 200 错误代码,fetch() 也不会拒绝。尝试在 JS 控制台中运行 fetch('https://httpbin.org/status/403').then(response => console.log('resolved', response)).catch(error => console.error('rejected', error)); 并查看它记录的内容。 看来您是完全正确的,还有其他原因导致工作人员提供错误的内容。我需要进一步调查,但由于您的解决方案似乎是可行的方法,我会将其标记为已接受。谢谢! 对于任何阅读 cmets 的人 - 我们正在使用 nginx 代理后端(至少在开发中)所以使用这种方法它永远不会陷入困境,我需要嗅探 res 代码然后手动拒绝 - 像这样 --> developers.google.com/web/updates/2015/03/introduction-to-fetch 嘿韦斯顿——这是故意的。 Service Worker 大部分时间都没有运行,而您的提议需要在每次设备在线/离线时有效地“唤醒它”以触发该事件。 fetchmessage 之类的事件可以唤醒它,但仅此而已。

以上是关于在 Service Worker 中检测离线状态的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

认识 service worker

使用Service Worker和离线缓存Cache

Service Worker基础知识整理

Service Worker 文件离线事件

借助Service Worker和cacheStorage缓存及离线开发 (转载)

Service Worker 无法在节点 js 服务器的离线模式下工作