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

2020-09-03
Header: vue-pwa

This article is a bit old and the content may be outdated, so please refer to it with caution and remember to check the latest official materials (such as documentation, etc.)

虽然前两节的内容足以写出能用的 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