maxEntries 为 1 的 Workbox ExpirationPlugin 似乎不对条目数强制执行任何限制

Workbox ExpirationPlugin with maxEntries of 1 seems to not enforce any limit on the number of entries

在 PWA 中,我有一个定期更新的大数据文件。我认为将最新版本缓存在我的 service worker 中以提供离线支持会很好,但只有最新版本。我不想使用磁盘 space.

挂起旧版本的数据文件

我正在使用 Workbox(版本 6.1.2),所以我尝试在我的 service worker 中编写:

registerRoute(
    new RegExp("/gen/real-player-data-*"),
    new CacheFirst({
        cacheName: "real-player-data",
        plugins: [
            new ExpirationPlugin({
                maxEntries: 1,
                purgeOnQuotaError: true,
            }),
        ],
    }),
);

为了完整起见,我的全职服务人员是:

import * as googleAnalytics from "workbox-google-analytics";
import { ExpirationPlugin } from "workbox-expiration";
import {
    cleanupOutdatedCaches,
    createHandlerBoundToURL,
    precacheAndRoute,
} from "workbox-precaching";
import { CacheFirst } from "workbox-strategies";
import { NavigationRoute, registerRoute } from "workbox-routing";

registerRoute(
    new RegExp("/gen/real-player-data-*"),
    new CacheFirst({
        cacheName: "real-player-data",
        plugins: [
            new ExpirationPlugin({
                maxEntries: 1,
                purgeOnQuotaError: true,
            }),
        ],
    }),
);

// Will be filled in by tools/build-sw.js
precacheAndRoute(self.__WB_MANIFEST);

const handler = createHandlerBoundToURL("/index.html");
const navigationRoute = new NavigationRoute(handler, {
    denylist: [
        new RegExp("^/files"),
        new RegExp("^/fonts"),
        new RegExp("^/gen"),
        new RegExp("^/ico"),
        new RegExp("^/img"),
        new RegExp("^/manifest"),
        new RegExp("^/sw.js"),
    ],
});
registerRoute(navigationRoute);

// https://developers.google.com/web/tools/workbox/guides/migrations/migrate-from-v3
cleanupOutdatedCaches();

googleAnalytics.initialize();

我的数据文件被命名为 /gen/real-player-data-HASH.json 所以我想这会做我想做的事 - 当我的应用程序请求我的数据文件的新版本时注意,将它添加到缓存,并删除旧版本。

在实践中,这似乎只能部分奏效。它确实创建了缓存并将数据存储在那里,但文件的旧版本似乎永远不会被删除。

自己试试吧。在写这个问题的时候去https://play.basketball-gm.com/new_league/real will install the service worker and request the data file, if you let it fully load. You might need to reload it once to see it show up in the Chrome dev tools. The latest version of the data file is https://play.basketball-gm.com/gen/real-player-data-2a6c8e9b0b.json

(旁注 - 我不确定为什么它说 Content-Length 为 0,您可以在底部窗格中清楚地看到数据。)

现在,安装了 service worker 后,如果你只是转到我的数据文件的旧版本,比如 https://play.basketball-gm.com/gen/real-player-data-540506bc45.json,它应该可以通过上面定义的路由获取,我相信它应该会导致以前的文件正在从缓存中删除。

它确实被拾取了,但是现在缓存中有两个条目,另一个没有被删除:

它们不仅仅是空的占位符,您可以在底部窗格中查看这两个文件中的数据。

尝试越多,列表越多,似乎没有限制:

https://play.basketball-gm.com/gen/real-player-data-18992d5073.json

https://play.basketball-gm.com/gen/real-player-data-fe8f297ea7.json

https://play.basketball-gm.com/gen/real-player-data-fd28409152.json

我在 Ubuntu 上使用 Chrome 89。

知道我做错了什么吗?还是有更好的方法来实现我的目标?

隔天更新

我在 service worker 中做了一些 console.log 调试。似乎 Workbox 基本上可以正常工作,除了 this block of code which is what actually deletes old entries from the cache:

    for (const url of urlsExpired) {
      await cache.delete(url, this._matchOptions);
    }

这是我为了调试而编辑的:

    console.log('urlsExpired', urlsExpired);
    console.log('keys', await cache.keys());
    for (const url of urlsExpired) {
      console.log('delete', url, this._matchOptions);
      const deleted = await cache.delete(url, this._matchOptions);
      console.log('after delete', url, deleted);
    }
    console.log('keys2', await cache.keys());

这是我在执行上面写的操作时看到的输出(加载服务工作者,加载第一个数据文件,加载第二个数据文件,观察这个输出,因为它尝试删除第一个数据文件但失败了来自缓存):

因此它确实识别出需要从缓存中删除的旧 URL。它确实在缓存中看到了旧的和新的 URLs。但是 cache.delete 调用解析为 falseMDN says:

resolves to true if the cache entry is deleted, or false otherwise

This article says:

If it doesn't find the item, it resolves to false.

所以我想这意味着它没有找到该项目?但是看看屏幕截图,URL 匹配缓存中的一个条目。还有 MDN says the first argument to cache.delete can be a Request object or a URL.

这是 Chrome 中的错误吗?工作箱中的错误?还有别的吗?我没主意了。

问题是在我对数据文件的响应中设置了“Vary”header,这意味着 ExpirationPlugin 将无法工作,除非 the ignoreVary option is enabled.