有没有自动更新(或者在iOS上清除缓存)PWA的方法?

22

我在iOS上遇到了一些问题,这在Android上很容易解决:当有新版本时,让我的PWA自动更新。我不确定在iOS上是否可能实现这一点。我使用vue.js和Quasar构建我的应用程序,在Android上一切都可以直接使用。当前,iOS版本的情况如下:

  1. 我可以检查我的服务器版本并将其与应用程序中存储的当前版本(indexedDB)进行比较,然后弹出通知表明有新版本可用。目前为止一切都好。
  2. 除了要求用户手动清除Safari缓存外,我找不到任何以编程方式从应用程序内部清除PWA缓存或以其他方式强制上传的方法。

因此,我现在的问题是:

  1. 有人能够让在iOS(11.3或更高版本)上的PWA当有新版本发布时自动更新吗?
  2. 是否有一种方法可以从我的PWA内部清除(Safari)应用程序缓存?

显然,通知用户必须在应用程序外执行几个步骤才能更新,这是一个非常糟糕的用户体验,但似乎这就是iOS目前的状态,除非我漏掉了某些东西。有没有人能够使这项工作在任何地方实现?


我认为当你打开应用程序时,它应该在后台更新。然后在你关闭应用程序之后,下一次打开它时,你应该看到更改。这不是发生了吗? - Mathias
2
是的,它应该可以,但实际上并不行。我从任何人那里都没有看到过这在iOS上能够工作的证据。你有一个可行的例子吗?(顺便说一下,我们正在谈论一个带有服务工作者的PWA,因此它应该也进行缓存。) - Stephen
不,我没有现在的iOS设备。只有一个旧的iPod,抱歉。 - Mathias
iOS 14的滑动应用程序删除状态,而对于旧版本,则需要重新启动手机。 - Ali
3个回答

34

经过数周的搜索,我终于找到了一个解决方案:

  1. 我在服务器上添加了版本字符串的检查,并像上面提到的那样将其返回给应用程序。

  2. 我在localstorage(IndexedDB)中查找它,如果没有找到,我就添加它。 如果找到了,我就比较版本号,如果服务器上有更新的版本,则弹出对话框。

  3. 关闭此对话框(我的按钮标记为“更新”)会运行window.location.reload(true),然后将新的版本字符串保存在localstorage中。

哇!我的应用程序已经更新了!我简直不敢相信问题竟然如此简单,我无论如何都找不到解决方法。希望这能帮助其他人!

2019年9月更新:

上述技术存在一些问题,特别是它绕过了PWA服务工作器机制,有时重载不会立即加载新内容(因为当前SW不会释放页面)。 我现在有了一个在所有平台上似乎都有效的新解决方案:

function forceSWupdate() {
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.getRegistrations().then(function (registrations) {
      for (let registration of registrations) {
        registration.update()
      }
    })
  }
}
forceSWupdate()

在我的serviceworker里,如果有更新,我现在会弹出对话框,然后从那里执行location.reload(true)。这总是立即刷新我的应用程序(重要的是,我已经添加了skipWaitingclientsClaim指令到注册中)。

这适用于所有平台,并且我可以编程方式检查更新或等待服务工作者自行完成(尽管它检查更新的时间因平台、设备和其他不可知因素而异,但通常不超过24小时)。


3
虽然自动更新不会完全无声无息,但如果您一天内多次部署前端页面,用户可能会厌倦频繁点击更新按钮。您有何想法? - Chris G.
你是否将 forceSWupdate() 放在版本检查方法中? - Tim Wickstrom
@TimWickstrom 我正在检查版本字符串(在服务器上的文件中,与PWA本身的缓存签名分开)在服务工作者中被调用的地方,以便在有新版本时向用户发出警报。forceSWupdate()方法只是强制服务工作者运行其更新后的方法,一旦它已经知道有一个更新。另外,我可以在服务器上的文件中检查版本并在发现与本地不同的情况下强制更新,但我还没有需要这样做。 - Stephen
@TimWickstrom,说实话,如果您不关心设置自己的版本字符串(仅在PWA上缓存签名不同时进行检查),那么您可以完全放弃它。 - Stephen
你能分享一下你的整个解决方案吗?我非常感兴趣。 - abr
显示剩余6条评论

5

如果还有人遇到这个问题,registration.update() 对我无效。我使用了完全相同的解决方案,但当服务器版本与本地存储的版本不匹配时,我必须取消注册服务工作者才能使其正常工作。

  if ('serviceWorker' in navigator) {
   await this.setState({ loadingMessage: 'Updating Your Experience' })
   navigator.serviceWorker.getRegistrations().then(function(registrations) {
    registrations.map(r => {
      r.unregister()
    })
   })
   await AsyncStorage.setItem('appVersion', this.state.serverAppVersion)
   window.location.reload(true)
  } 

当应用程序重新加载时,服务工作者将会被重新注册,iOS Safari浏览器和“已加入书签”的渐进式Web应用程序将显示应用程序的当前版本。


3

除了提示用户有新版本可用之外,您还可以扩展“激活”事件侦听器,以在发布新的serviceworker版本时删除旧缓存。

  1. 添加版本和名称变量
var version = "v3" // increase for new version
var staticCacheName = version + "_pwa-static";
var dynamicCacheName = version + "_pwa-dynamic";
  1. 在当前版本不适用的情况下,删除缓存:
self.addEventListener('activate', function(event) {
    event.waitUntil(
        caches.keys().then(function(cacheNames) {
            return Promise.all(
                cacheNames.filter(function(cacheName) {
                    if (!cacheName.startsWith(staticCacheName) &&
                        !cacheName.startsWith(dynamicCacheName)) {
                        return true;
                    }
                }).map(function(cacheName) {
                    console.log('Removing old cache.', cacheName);
                    return caches.delete(cacheName);
                })
            );
        })
    );
});

为了使此功能适用于iOS Safari浏览器和“已加为书签”的PWA,我只是添加了@jbone107提供的稍微简化的函数:(来源:https://dev59.com/3FcO5IYBdhLWcg3wpjIz#45468998
self.addEventListener('activate', function(event) {
    event.waitUntil(
        caches.keys().then(function(cacheNames) {
            return Promise.all(
                cacheNames.filter(function(cacheName) {
                    if (!cacheName.startsWith(staticCacheName) &&
                        !cacheName.startsWith(dynamicCacheName)) {
                        return true;
                    }
                }).map(function(cacheName) {
                    // completely deregister for ios to get changes too
                    console.log('deregistering Serviceworker')
                    if ('serviceWorker' in navigator) {
                        navigator.serviceWorker.getRegistrations().then(function(registrations) {
                            registrations.map(r => {
                                r.unregister()
                            })
                        })
                        window.location.reload(true)
                    }

                    console.log('Removing old cache.', cacheName);
                    return caches.delete(cacheName);
                })
            );
        })
    );
});

这样,您只需增加版本号,服务工作者就会自动更新。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接