即使在加载新版本后,Service Worker 仍可能显示旧版本应用程序的情况?

Situations in which service worker may display old version of app even after new version is loaded?

抱歉,我没有这个问题的可重现测试用例,因为我自己从未见过它发生过。我只知道它发生是因为客户端登录我的应用程序和用户的投诉。

问题是:

  1. 我部署了新版本的应用程序
  2. 用户访问我的站点,获取新版本,运行成功
  3. 用户再次访问我的网站并获得我的应用程序的旧版本

我正在使用 service worker,我希望它能提供一些有关未发生的情况的保证运行。当新版本包含 IndexedDB 模式迁移时尤其令人不安,因为那样我的应用程序的旧版本将不再工作。

更多详情:

我正在使用 Workbox 4.3.1。我的服务人员基本上是:

importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
workbox.precaching.precacheAndRoute([]);
workbox.routing.registerNavigationRoute("/index.html", {
    blacklist: [
        new RegExp("^/static"),
        new RegExp("^/sw.js"),
    ],
});

workbox.precaching.precacheAndRoute([]);workboxBuild.injectManifest 填充。我可以手动确认是否填写了正确的文件。服务人员通常可以正常工作。我可以在浏览器开发工具中看到它。我可以断开与 Internet 的连接并继续使用我的应用程序。一切似乎都很好。就像我上面说的,我从来没有见过这个问题发生,我也没有可重现的测试用例。

但是我的一些用户遇到了上述问题。我尝试使用客户端错误日志记录来进行调查。我在我的应用程序中添加了一些代码以将其版本号存储在 localStorage 中,并在初始加载时将该版本号与当前 运行 版本号进行比较。如果 localStorage 中的版本比当前 运行 版本更新(即它成功地 运行 过去的较新版本但现在又回到旧版本),它会记录版本号一些额外的信息:

let registrations = [];
if (window.navigator.serviceWorker) {
    registrations = await window.navigator.serviceWorker.getRegistrations();
}
log({
    hasNavigatorServiceWorker:
        window.navigator.serviceWorker !== undefined,
    registrationsLength: registrations.length,
    registrations: registrations.map(r => {
        return {
            scope: r.scope,
            active: r.active
                ? {
                      scriptURL: r.active.scriptURL,
                      state: r.active.state,
                  }
                : null,
            installing: r.installing
                ? {
                      scriptURL: r.installing.scriptURL,
                      state: r.installing.state,
                  }
                : null,
            waiting: r.waiting
                ? {
                      scriptURL: r.waiting.scriptURL,
                      state: r.waiting.state,
                  }
                : null,
        };
    }),
})

查看我的日志,我发现只有大约 1% 的用户会出现此问题。 Firefox 在这些用户中丰富(占总流量的 4%,但占此问题日志条目的 18%),但它发生在所有浏览器和操作系统中。

而且我看到几乎所有记录都有这些值:

{
    hasNavigatorServiceWorker: true,
    registrationsLength: 1,
    registrations: [{
        "scope": "https://example.com/",
        "active": {
            "scriptURL": "https://example.com/sw.js",
            "state": "activated"
        },
        "installing": null,
        "waiting": null
    }]
}

据我所知,这些都是正确的值。

我还应该注意,我的 JavaScript 文件在 URL 中有一个散列,所以当用户请求新版本。

那么,可能发生了什么?如何解释这种观察到的行为?我还可以记录什么以进一步调试它?

我能想出的唯一场景对我来说似乎非常难以置信。喜欢...

  1. 用户加载 v1,service worker 因某种原因失败
  2. 用户加载 v2,service worker 因某种原因失败
  3. 用户以某种方式从他们的浏览器缓存中获取了 v1,因为之前所有的 service worker 都失败了,但现在 service worker 可以正常工作并将其存储为当前版本

但我没有证据表明 service worker 曾经失败过。我的错误日志中没有任何内容。没有用户抱怨离线支持被破坏。

如果有帮助,实际发生这种情况的网站是 https://play.basketball-gm.com/, the service worker is at https://play.basketball-gm.com/sw.js, and all the code is available on GitHub

自从大约一年前我开始使用 Service Worker 以来,这个问题就一直存在。我终于开始写一个 Stack Overflow 问题了,因为我已经放弃了我能够自己解决这个问题甚至创建一个可重现的测试用例的希望。

用户以某种方式从他们的缓存中获取旧版本。删除过时的缓存应该可以解决问题。一旦安装了新的 service worker 并且没有使用以前的版本,新的就会激活,你会得到一个 activate 事件。因为旧版本不在了,现在是删除未使用缓存的好时机。

self.addEventListener('activate', function(event) {
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.filter(function(cacheName) {
          // Return true if you want to remove this cache,
          // but remember that caches are shared across
          // the whole origin
        }).map(function(cacheName) {
          return caches.delete(cacheName);
        })
      );
    })
  );
});

在激活期间,其他事件(例如 fetch)被放入队列中,因此长时间激活可能会阻止页面加载。尽可能精简您的激活,仅将其用于您在旧版本处于活动状态时无法执行的操作。

在我看来,这可能更像是 'tech support' 为遇到问题的用户提供的答案。

在开发过程中尝试在本地重新加载我自己的 React 应用程序时,我个人遇到了类似的问题 运行。 (客户端代码)我正在使用隧道服务 ngrok。

我的站点将继续运行,即使在我使用旧版本代码终止服务后也是如此。 (我发现这令人沮丧,在实施修复后它似乎不起作用)

(技术支持)回答:

可能需要执行 'Hard Reload',有时 'Empty Cache and Hard Reload',这应该会清理浏览器。(这实际上取决于结构随网站加载的文件)。

您可以在开发者控制台打开时访问这些选项。

有关硬刷新的更多详细信息,您可以参考此SO post:whats-the-difference-between-normal-reload-hard-reload-and-empty-cache-a

一年后,我终于弄明白了。

我正在预缓存被某些广告拦截器拦截的资产。这导致 service worker 安装失败,使旧 service worker 的使用时间比预期的要长。不知何故,我的客户端错误日志记录没有捕获到此故障。