首页 > 解决方案 > 如何将应用程序缓存清单转换为服务工作者

问题描述

应用程序缓存现已弃用, Chrome等浏览器正在移除支持

我们有一个应用程序可以 100% 离线工作,同时将数据存储在 indexeddb 中,并稍后在用户重新在线时同步。我们需要将此站点从使用应用程序缓存转换为服务工作者。我们将为我们的服务人员使用Workbox

我们必须隐藏缓存清单的三个主要部分。

CACHE 部分 这是要预缓存的资产列表。这可能是最直接的过渡,因为我们使用工作箱来预缓存这些文件

NETWORK 部分 我们在这里使用 * (可能是最常见的),所以这可能不会成为问题。

FALLBACK 部分 我们在后备部分中有很多条目。基本上,它们被重定向到登录页面,以防有人离线刷新页面。

例子:

FALLBACK:
/search /login
/customer-edit /login
/foo-bar-baz /login
...

我的问题:

是否有 1) 将应用程序缓存/缓存清单转换为服务工作者的一般指南,或者 2) 将 FALLBACK 部分转换为服务工作者中的等效功能的一些具体指南。

Google 和 Duck Duck Go 的帮助不大。

现有项目将应用程序缓存升级到服务工作者,但大多数看起来都是测试版,例如来自 Google Chrome 实验室:github.com/GoogleChromeLabs/sw-appcache-behavior

标签: htmlservice-workeroffline-cachinghtml5-appcache

解决方案


这是我使用Google 的 workbox提出的解决方案。

旁注:Workbox 似乎为大多数常见的 service worker 用例提供了解决方案,并且具有非常灵活的分发模型,无论是在 vanilla js 环境中还是在您选择的框架中,它都非常容易使用。

我们最终将我们的服务器端 AppCache(缓存清单代码)转换为生成服务工作者。(如何预缓存来自 json 文件的 url 列表?

根据您的服务器端语言,您的代码会有所不同,但这是对我们有用的最终产品:

service-worker.js(生成的服务器端)


    const productVersion = "3.01";

    importScripts('/assets/workbox/workbox-sw.js');

    workbox.setConfig({
        modulePathPrefix: '/assets/workbox/'
    });

    const { precacheAndRoute, createHandlerBoundToURL } = workbox.precaching;
    const { NavigationRoute, registerRoute, setCatchHandler } = workbox.routing;

    precacheAndRoute([
        // cache index html
        {url: '/', revision: '3.01' },

        // web workers
        {url: '/assets/some-worker.js?ver=3.01', revision: '' },
        {url: '/assets/other-worker.js?ver=3.01', revision: '' },
        // other js files
        {url: '/assets/shared-function.js', revision: '3.01' },
        // ... removed for brevity
        // css
        {url: '/assets/site.css', revision: '3.01' },
        {url: '/assets/fonts/fonts.css', revision: '3.01' },
        // svg's
        {url: '/assets/images/icon.svg', revision: '3.01' },
        {url: '/assets/images/icon-2.svg', revision: '3.01' },

        // png's
        {url: '/assets/images/img-1.png', revision: '3.01' },
        {url: '/assets/images/favicon/apple-touch-icon-114x114.png', revision: '3.01' },
        // ...
        // ...

        // fonts
        {url: '/assets/fonts/lato-bla-webfont.eot', revision: '3.01' },
        {url: '/assets/fonts/lato-bla-webfont.ttf', revision: '3.01' },

        // sounds
        {url: '/assets/sounds/keypress.ogg', revision: '3.01' },
        {url: '/assets/sounds/sale.ogg', revision: '3.01' },
    ]);

    // Routing for SPA
    // This assumes DEFAULT_URL has been precached.
    const DEFAULT_URL = '/';
    const handler = createHandlerBoundToURL(DEFAULT_URL);
    const navigationRoute = new NavigationRoute(handler, {
        denylist: [
            new RegExp('/ping'),
            new RegExp('/upgrade'),
            new RegExp('/cache.manifest'),
      ],
    });
    registerRoute(navigationRoute);

    // This allows the main window to signal the service worker that
    // it should go ahead and install if it's waiting.
    addEventListener('message', (event) => {
        if (event.data && event.data.type === 'SKIP_WAITING') {
            skipWaiting();
        }
    });


  1. 还有一些其他的事情需要注意。我们必须弄清楚如何从 App?Cache 平滑升级到 Service Worker。事实证明,为我们生成一个空的缓存清单起到了作用。
  2. 我们已经有一个升级过程(提示用户升级或使用倒计时强制自动升级),所以我们必须做一些工作才能让服务人员使用它。注意服务工作者文件的末尾有 addEventListener代码。我们实际上是从升级页面调用它以获得平滑的升级过程。它是这样的:

A)升级脚本检测到新版本可用(有很多方法可以做到这一点,api调用轮询等)

B)如果用户接受或计时器到期,则将用户重定向到upgrade页面。这一步至关重要,因为您无法在应用仍在运行的情况下更新服务人员。所以导航到升级页面,等待服务工作者安装,然后告诉它跳过等待并重定向到主(登录)屏幕。

C) 用户正在愉快地运行新版本的应用程序。

升级页面代码:(这是显示某种类型的“更新”用户界面的好页面)

<script type="module">
        import { Workbox } from '/assets/workbox/workbox-window.prod.mjs';

        if ('serviceWorker' in navigator) {
            const wb = new Workbox('/serviceworker');

            // This code exists b/c a service worker can't update with just a refresh/reload in the
            // browser. This is b/c on a reload, the old and new page exist simultaneously and the old MUST
            // unload before the new service worker can automatically assume control. Also if multiple pages
            // are open, this blocks the service worker from taking control (multiple pages should not an issue with this app).
            // This code activates a waiting service worker and _then_ redirects back to the app.

            // Add an event listener to detect when the registered
            // service worker has installed but is waiting to activate.
            wb.addEventListener('waiting', (event) => {

                // Set up a listener that will reload the page as soon as the previously waiting
                // service worker has taken control.
                wb.addEventListener('controlling', (event) => {
                    window.location.replace('/login');
                });

                // Send a message telling the service worker to skip waiting.
                // This will trigger the `controlling` event handler above.
                wb.messageSW({type: 'SKIP_WAITING' });

            });

            wb.register();
        }

        // set a timeout in case the service worker has already installed.
        setTimeout(function () {
            window.location.replace('/login');
        }, 30000);
</script>

主页面(index.html 等)(如果用户来到应用程序并准备激活服务器工作者,因此需要刷新才能加载正确的资产/代码)

<script type="module">
        import { Workbox } from '/assets/workbox/workbox-window.prod.mjs';

        if ('serviceWorker' in navigator) {
            const wb = new Workbox('/serviceworker');

            wb.addEventListener('activated', (event) => {
                // `event.isUpdate` will be true if another version of the service
                // worker was controlling the page when this version was registered.
                if (!event.isUpdate) {
                    // service worker was updated and activated for the first time.
                    // If your service worker is configured to precache assets, those
                    // assets should all be available now.
                    // this will only happen if the browser was closed when a new version was made available
                    // and it will only happen once per service worker install.
                    // Reload to so all libs are correct version.
                    window.location.reload(true);
                }
            });

            wb.register();
        }
    </script>


推荐阅读