如何让使用Service Worker的PWA网页乖乖更新?

      ☕ 4 分钟

你在适配了PWA的网页上有没有遇到这样的情况?控制台一直显示:

New content is available; please refresh.

但是不管怎么按F5刷新提示都不变化,内容也不更新?

快速修正

如果你赶时间或者并不想刨根问底,问题这样解决:彻底关闭你的浏览器(注意不是标签页),再打开它。

现在,网页的内容应该正常更新了。

问题的根源

PWA(Progressive Web Apps)网页的离线能力和本地缓存特性由service worker提供,你正关注的网页的新内容即将由一个新版本的service worker提供,但是这个service worker正等待当前网页上的前任service worker让出控制权。乍一看,这个过程似乎很碍事,但是这背后其实另有考虑。

Service Worker生命周期

Jake Archibald 写了一篇讲解service worker的文章,文章由浅入深,绝对值得一读。归纳起来,service worker的生命周期由下面这些状态组成:

stateDiagram
[*] --> installing: 页面JS调用navigator.serviceWorker.register()
note right of installing
  新版本service worker
  获得"install"事件
end note
installed: installed/waiting
installing --> installed: installEvent.waitUntil()获得成功的promise
installed --> activating: 等待,直到上一任service worker控制0个客户端
note right of activating
  新版本service worker
  获得"activate"事件
end note
activating --> activated: activateEvent.waitUntil()获得成功的promise
note right of activated
  新版本service worker 
  开始控制客户端
end note
activated --> redundant: 被下一版本的service worker替代
redundant --> [*]
  • installing: 页面JS调用navigator.serviceWorker.register(), 浏览器下载并解析相关脚本,我们的新版本service worker获得install事件,它须要为事件的installEvent.waitUntil()方法提供一个promise,以示意浏览器service worker本身安装是否成功;
  • installed/waiting: 状态在installEvent.waitUntil()的promise成功之后触发。倘若此时已经存在一个先前版本的service worker,浏览器会让我们的新版本service worker等待,直到先前版本控制0个客户端(客户端一般指service worker所在域名下的tab页面)。这里就是阻挡你的内容更新的地方。
  • activating: service worker获得activate事件,此时先前版本的service worker(如果有)已经让出控制权。我们的新版本service worker会在这个状态中做一些承前启后的准备工作,例如清理旧版本留下的缓存,接管已经开启的tab页面。它会用传递给activateEvent.waitUntil()的promise来示意浏览器是否准备就绪;
  • activated: 新版本service worker接管客户端,处理从客户端传递的fetchpush等事件;
  • redundant: service worker的废弃状态,原因可能是安装失败或被新版本替代。

为何浏览器不直接更新Service Worker?

开发者面板中正在等待更新的service worker的信息
开发者面板中正在等待更新的service worker的信息

浏览器让新版本service worker等待以避免页面和新旧版本service worker三者之间的行为冲突。

试想浏览器默认让新版本service worker直接踢掉旧版本,立刻接管所有已经存在的客户端,这些已经存在的客户端可能无法适应这个改变,因为它们的脚本逻辑、缓存逻辑匹配的是旧版本的service worker。而且,在新service worker中采用积极的版本兼容方案并不容易,因为你的用户可能正在使用几个版本号之前的service worker.

于是,浏览器选了这一条更少惊喜(吓)的路子。

为什么按F5刷新无效?

抬头看看地址栏,地址栏里的URL在刷新前后都不改变,于是当前tab会继续做旧版本service worker的客户端。

如果你的用户只在你的网站上开启了一个tab,那么在他/她导航该tab到其他网站之后,和重启浏览器一样,旧版本service worker也会让出控制权(因为没有客户端了)。

经验总结

如果你正担心使用PWA会让你的用户无法获得内容更新,那么你大可放轻松。你的用户会在他下次启动浏览器时获得更新。

如果你感到等待的过程太过漫长,你可以考虑提示你的用户主动更新,但一定注意skipWaiting可能会带来我们上面提到的惊喜(吓)。

另外,强制刷新(shift + F5)会使得当前页面完全不使用service worker。这是service worker规范里要求的特性,支持service worker的浏览器都会支持它。


nanmu42
作者
nanmu42
用心构建美好事物。

目录