简体   繁体   English

PWA 在 Safari 发布新版本后一直显示旧版本

[英]PWA keeps showing the old version after a new release on Safari

I added PWA and service worker to my existing web app based on ReactJs and nextJs.我基于 ReactJs 和 nextJs 将 PWA 和 service worker 添加到我现有的 web 应用程序中。 Everything looks fine on the first release.第一次发布时一切看起来都很好。 However, when I try to release updates, something strange is happening.但是,当我尝试发布更新时,发生了一些奇怪的事情。

After each release, I got an alert asking me to update to a new version.每次发布后,我都会收到一条提醒,要求我更新到新版本。

However when a new version comes up eventually my app is updated, but it falls back again to the old version after I close and reopens it.但是,当最终出现新版本时,我的应用程序会更新,但在我关闭并重新打开它后它会再次退回到旧版本。

It should be noted that this happens only on some phones.应该注意的是,这只发生在某些手机上。

Please help me to solve it.请帮我解决。

My SW:我的软件:

navigator.serviceWorker
        .getRegistrations()
        .then(function (registrations) {
                return Promise.all(registrations.map(function(r) {return r.unregister()}));
            })
        .then(function() {
                return navigator.serviceWorker.register(serviceFilePath, {
                            scope: _this.options.scope
                }).then(function (registration) {
                    console.info("Service worker has been registered for scope: " + registration.scope);
                    if (needsUpdate) {
                            return registration.update().then(function () {
                            _this.reload();
                            window.parent.postMessage("new-version", "*");
                            console.info("Service worker has been updated.");
                            window.location.reload();
                            return true;
                });
            }
            return true;
        });
});


ServiceWorker.prototype.reload = function () {
            var _this = this;
            return this.getOptions(true).then(function (options) {
                return Promise.all([
                    _this.preload(),
                    // _this.checkPersistence(),
                    _this.checkPushServiceStatus(false)
                ]).then(function () {
                    _this.debug("Clear old caches... (< v" + options.version + ")");
                    var promises = [];
                    for (var i = 0; i < options.version; i++) {
                        promises.push(new AdvancedServiceWorker.BrowserCache(options.storageName, i).remove());
                    }
                    return Promise.all(promises).then(function () {
                        return true;
                    });
                });
            }).catch(function () {
                return false;
            });
        };
        
        
        
BrowserCache.prototype.remove = function (condition) {
            if (!condition) {
                return caches.delete(this.storageName);
            } else if (typeof condition === "string" ||
                condition instanceof String ||
                condition instanceof RegExp) {
                if (!(condition instanceof RegExp)) {
                    condition = AdvancedServiceWorker.Condition.wildCardToRegEx((condition));
                }
                return this.open().then(function (cache) {
                    return cache.keys().then(function (keys) {
                        var promises = [];
                        keys.forEach(function (request) {
                            if (request.url && condition.test(request.url)) {
                                promises.push(cache.delete(request));
                            }
                        });
                        if (!promises.length) {
                            return Promise.resolve(false);
                        }
                        return Promise.all(promises).then(function (results) {
                            for (var i = 0; i < results.length; i++) {
                                if (results[i]) {
                                    return true;
                                }
                            }
                            return false;
                        }, function () {
                            return false;
                        });
                    });
                });
            } else if (condition instanceof Array && condition.length) {
                return this.open().then(function (cache) {
                    var promises = [];
                    for (var i = 0; i < condition.length; i++) {
                        promises.push(cache.delete((condition[i])));
                    }
                    return Promise.all(promises).then(function (results) {
                        for (var j = 0; j < results.length; j++) {
                            if (results[j]) {
                                return true;
                            }
                        }
                        return false;
                    }, function () {
                        return false;
                    });
                });
            } else {
                return Promise.resolve(false);
            }
        };

Ok, my service worker below.好的,下面是我的服务人员。

Some things to consider, as this is a SW that I've made for my own needs, as such you might need to tweak for yours.需要考虑的一些事情,因为这是我根据自己的需要制作的软件,因此您可能需要根据自己的需要进行调整。

It's designed to work for a simple SPA, that loads all it's resource's on page load.它旨在为一个简单的 SPA 工作,在页面加载时加载它的所有资源。 Generally speaking this is how most SPA's will work.一般来说,这是大多数 SPA 的工作方式。 But if you have resources that you say load dynamically via fetch etc, you will want to add those files manually using cache.addAll , some JS frameworks do load routes dynamically, so just be aware.但是如果你有你说通过fetch等动态加载的资源,你会想要使用cache.addAll手动添加这些文件,一些 JS 框架会动态加载路由,所以请注意。

Because I'm loading the resources automatically and not using addAll in theory you could have an offLine app that's not got all it's resources fully loaded, eg.因为我是自动加载资源而不是在理论上使用addAll你可能有一个离线应用程序没有完全加载它的所有资源,例如。 if you just happen to loose inte.net during update.如果您在更新期间碰巧丢失了 inte.net。 In practice this is not likely, and if happens a simple refresh would fix, but if you do want to make sure everything is loaded you will have to manually add them using cache.addAll , see comments in file for were to do that.实际上这不太可能,如果发生这种情况,简单的刷新就可以解决,但是如果您确实想确保所有内容都已加载,则必须使用cache.addAll手动添加它们,请参阅文件中的注释以了解是否这样做。

Enough explaining, here is the code.足够的解释,这是代码。 -> ps, it's also using Typescript here, but you can remove that easily by deleting the declare on the first line. -> ps,它在这里也使用 Typescript,但您可以通过删除第一行的声明轻松删除它。 (I just like my types). (我只是喜欢我的类型)。

declare var self: ServiceWorkerGlobalScope;

//remember to update version number if updating.
const cacheName = "my-spa-v1.0.0";

self.addEventListener("install", (e) => {
  e.waitUntil(
    (async () => {
      const cache = await caches.open(cacheName);
      //if you want to manually add resources
      //await cache.addAll([contentToCache,...]);
      return self.skipWaiting();
    })()
  );
});

//lets keep our cache's clean
self.addEventListener("activate", (e) => {
  e.waitUntil(
    caches.keys().then((keyList) => {
      return Promise.all(
        keyList.map((key) => {
          if (key === cacheName) {
            return;
          }
          return caches.delete(key);
        })
      );
    })
  );
});

//lets auto cache GET requests
self.addEventListener("fetch", (e) => {
  e.respondWith(
    (async () => {
      const r = await caches.match(e.request);
      if (r) {
        return r;
      }
      const response = await fetch(e.request);
      const cache = await caches.open(cacheName);
      if (e.request.method === 'GET')
        cache.put(e.request, response.clone());
      return response;
    })()
  );
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM