Header: vue-pwa

用 Vue 做 PWA (三):理解生命周期

2020-09-03

虽然前两节的内容足以写出能用的 service worker,但是如果深入细节,仍然会有一些“匪夷所思”的现象发生。

This series contains the following posts:

 

理解 service worker 的生命周期,可以帮助掌握其行为,以达到更好的体验。

本文主要概括 https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle

理解 service worker 的“哲学” #

概括来讲就是离线优先和一致性。离线优先没说的,一致性体现在:

  1. 页面打开是什么(用不用 service worker,用哪个版本的)就一直是什么
  2. 所有(同一个作用域内的)标签页用同一个版本的 service worker
  3. 当然新的不能影响正在运行的旧的 service worker

知识补充:precache 机制 #

Precache 简单讲就是在 service worker 安装的时候预先下载需要的文件( JS, CSS 之类),保证安装成功的时候这些文件都已经缓存完毕,一旦 service worker 激活即可直接从 cache 中获取。

cli-plugin-pwa 用到了 workbox-webpack-plugin。之所以成为 webpack-plugin,就是为了自动将打包出来的文件 precache。

安装成功不代表可用 #

按照第一条,由于第一次打开时是无 service worker 的,所以即使安装成功了,激活了,请求也不会走 service worker。必须刷新页面才行。

sw-install

不过可以用选项 clientsClaim 可以让 service worker 激活后立马接管页面,如果当前无人接管的话。需要注意的是,如果请求早于 service worker 安装并激活,显然即使激活后立马接管页面也无能为力了。而且如果 precache 的东西太多的话会严重减慢 service worker 的安装进度,不利于快速接管。

Scope #

当然安装成功了但是没法用还有一种可能,作用域不对。如果是注册 /assets/js/sw.js,那默认作用域就是 /assets/js/。解决方法有二:

  1. sw.js 放到应用根目录下
  2. 注册的时候说明 register('/assets/js/sw.js', { scope: '/' })

    // 或者翻译成 Vue 的
    register('/service-worker.js', {
      registrationOptions: { scope: './' },
       // ...
    })
    js

注意如果你的应用在 /app/ 下,注册了 /app/sw.js,但是某一次以 /app (没有末尾斜杠)访问主页,那么 service worker 是不会起作用的。

什么时候更新 #

这个简单情况很简单,只要访问到作用域内的页面即可触发浏览器查看更新。

不过 pushsync 的事件在一定条件下也可触发,但这是直接写 InjectManifest 的大佬的事情了。

还可以手动更新:

navigator.serviceWorker.register('/sw.js').then(reg => {
  // sometime later…
  reg.update();
})
js

或者翻译成 Vue 的:

import { register } from 'register-service-worker'

register('/service-worker.js', {
  registered (registration) {
    console.log('Service worker has been registered.')

    registration.update()
    // Or
    setTimeout(registration.update.bind(registration), 10000)
  }
})
js

更新完要重开才有效果 #

为什么安装是刷新就可以,但是更新是重开?因为在刷新的时候,旧页面和新页面是有交叠的,也就是说旧的 service worker 没法放手,要一直抓着,直到页面关闭。

sw-update

所以结合起来,要(正常)更新 service worker(也就是更新应用版本,因为 precache 机制),需要打开页面,稍等一会,等待新的 service worker 安装完成后

不过(在新的 service worker 上)用 skipWaiting 选项可以让新的 service worker 在安装成功后直接激活。但是也有可能使页面混乱,谨慎使用。

更新这么麻烦,开发测试的时候等不起啊 #

勾上 Update on reload #

这样每次刷新或访问新的页面都会触发更新,且无论 service worker 是否有变化都会重新安装并立即生效。

update-on-reload

手动 Skip waiting #

skip-waiting

强制刷新 #

不仅在开发时有用,还可以作为一个选项提供给用户,方便解决设计出错或用不上新功能给用户带来的烦恼。

location.reload(true)
js
👍
1
Leave your comments and reactions on GitHub