如何让使用Service Worker的PWA网页乖乖更新?
你在适配了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接管客户端,处理从客户端传递的
fetch
和push
等事件; - redundant: 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的浏览器都会支持它。