如何确保永不激活的更新的Workbox缓存更新

时间:2019-02-08 20:02:28

标签: javascript workbox

我一直在努力将必须拥有的Web应用程序转换为PWA,并且我正在使用Google的Workbox(v3.6.1)来预缓存资源。在大多数情况下,它运行良好,但是似乎有一种特定的情况导致缓存的文件不同步。

我正在使用基本的precacheAndRoute()功能来设置文件以进行预缓存。

workbox.precaching.precacheAndRoute([]); //populated at build time via workbox-cli

在首次安装和大多数更新时,文件将按预期进行预缓存。但是,如果当前有一个Service Worker实例waiting,并且已安装新的更新,则临时缓存中的所有挂起文件都将被删除,并且不会安装最新版本。

似乎workbox.precaching的安装步骤在将文件添加到临时高速缓存时更新了包含所有文件版本的IndexDB。因此,下一个Service Worker版本认为所有文件的先前最新版本当前都已缓存,即使它们仍仅在临时缓存中也是如此。然后,新安装会在插入自己的文件之前从临时缓存中删除所有内容。因此,先前waiting实例的暂挂缓存文件将永远丢失。

我的想法是,在安装新版本时,我可以强制临时缓存(通过使用PrecacheControlleractivate()函数同步到永久缓存),然后再允许新实例到预缓存,但我担心在用户积极使用该应用程序时更新永久缓存。

我要寻找的确认是我的想法是一个适当的解决方案,或者是有关如何处理这种情况的其他建议。

1 个答案:

答案 0 :(得分:0)

Workbox最近发布了4.0.0版,并且此升级似乎已解决此问题。我将保留下面的答案,因为它对于目前无法升级到4.0.0的任何人仍然有用。


就像我在问题中提到的那样,我使用PrecacheController或多或少地工作了。令人讨厌的一点是,由于我不再使用标准的fetch,因此我必须自己实现activateworkbox.precaching侦听器。如果有人有其他想法,请随时发布其他选项。

const precacheController = new workbox.precaching.PrecacheController();
precacheController.addToCacheList([]); //populated at build-time with workbox-cli

self.addEventListener('fetch', (event) => {
    var url = event.request.url.endsWith('/') ? event.request.url + 'index.html' : event.request.url;
    event.respondWith(new Promise(function (resolve) {
            if (precacheController.getCachedUrls().indexOf(url) > -1) {
                resolve(caches.open(workbox.core.cacheNames.precache)
                    .then((cache) => {
                        return cache.match(url);
                    })
                    .then((cachedResponse) => {
                        return cachedResponse || fetch(url);
                    }));
            } else {
                resolve(fetch(event.request));
            }
        }));
});

self.addEventListener('activate', (event) => {
    event.waitUntil(precacheController.activate());
    self.clients.claim();
});

self.addEventListener('install', function (event) {
    var timeoutId = null;
    event.waitUntil((new Promise(function (resolve, reject) {
                if (self.registration.waiting) {
                    var channel = new MessageChannel();
                    channel.port1.onmessage = function (event) {
                        resolve();
                    };

                    //tell the current 'waiting' instance to cleanup its temp cache
                    self.registration.waiting.postMessage({
                        action: 'cleanupCache'
                    }, [channel.port2]);
                } else {
                    resolve();
                }
            }))
        .finally(function () {
            //once temp cache is cleaned up from any 'waiting' instance, begin my install
            return precacheController.install();
        }));
});

self.addEventListener('message', function (event) {
    if (event.data.action === 'cleanupCache') {
    //move files from temp cache to permanent cache
        precacheController.activate().finally(function () {
            if (event.ports[0]) {
                event.ports[0].postMessage('cleanupComplete');
            }
        });
    }
});