神代綺凜

为 vue 项目添加 PWA 支持
前段时间摸鱼终于摸到了 Service Worker 这块,顺理成章的接触了 PWA,于是用这篇文章记录下如何为一...
扫描右侧二维码阅读全文
26
2019/06

为 vue 项目添加 PWA 支持

前段时间摸鱼终于摸到了 Service Worker 这块,顺理成章的接触了 PWA,于是用这篇文章记录下如何为一个 vue 应用添加 PWA 支持,包括中途遇到的一些坑点和一些技巧

Head Pic:【オリジナル】「Recital」/「RH」的插画 [pixiv]

vue & PWA

如果您的目的不是为现有的 vue 项目添加 PWA 支持,那么更推荐尝试 Lavas

注:PWA 应用要求必须全程 https,且在已安装的 PWA 应用中无法发送 http 请求

为已有项目添加 PWA 支持

1. 安装 PWA 插件

如果你已经在使用@vue/cli,那么可以直接在可视化界面中安装 PWA 插件

否则,可以通过vue add @vue/pwa命令来安装

该插件会使用谷歌的 PWA 框架 Workbox

2. 配置 PWA 插件

需要创建或修改项目中的vue.config.js,配置项以及示例在此处

我想多提几句的配置项有三个:

workboxPluginMode

可选配置项,默认为GenerateSW

GenerateSWInjectManifest如何选择:

  • 如果你只是想简单地将项目 PWA 化,选择GenerateSW,插件会自动帮你生成包含 precache manifest 的service-worker.js
  • 如果你有较高的定制需求,需要在已有 Service Worker 的基础上将项目 PWA 化,则选择InjectManifest,插件会在你指定的service-worker.js的基础上加入 precache manifest

workboxOptions

配置项,请对应workboxPluginMode来参考

通过配置可以做到的一些常用操作:

  1. 将指定(或指定文件夹中的)文件添加到 precache manifest 中,或从中排除,支持使用正则表达式
  2. 自动跳过 Service Worker 的等待阶段
  3. 添加离线 Google Analytics 支持
  4. 运行时缓存runtimeCaching,Workbox 的强大所在,阅读这些内容会使你更好地了解如何配置以及具体能做什么:

iconPaths

该设置项可以自定义在页面<head>中添加的一些图标的<link><meta>中指定的文件路径

public/icons中有安装插件时生成的默认图标

其有一个坑点,就是你无法设置不去添加某些<link><meta>,也就是强制性的

这主要会影响到maskIcon,是 Macbook 的 Touch Bar 上的图标,由于要求必须是 svg,个人开发的小应用一般懒得去制作这个图标,但又无法不去添加这个<link>

3. 配置manifest.json

位于public/manifest.json,安装插件时自动生成,参考 Web App Manifest 进行配置

引导用户添加 PWA 应用

在应用中可以自行通过提示等方式引导用户手动添加 PWA 应用,以下列举目前我所知道的添加方式

Chrome 专有方式

对于 PC 或 Android 的 Chrome 浏览器都可以实现点击一个按钮来添加 PWA 应用,其原理是拦截了beforeinstallprompt这一事件,并在自己需要的时候触发

例如我们可以在项目的根 vue 实例中(以下示例省略了挂载及渲染等操作)

new Vue({
    data: {
        deferredPrompt: false
    },
    created() {
        window.addEventListener('beforeinstallprompt', e => {
            e.preventDefault();
            this.deferredPrompt = e;
        });
    },
    methods: {
        installPWA() {
            if (this.deferredPrompt) {
                this.deferredPrompt.prompt();
                this.deferredPrompt = false;
            }
        }
    }
});

然后就可以像这样自由地在任何 template 中使用了

<button @click="$root.installPWA" :disabled="$root.deferredPrompt===false">添加到主屏幕</button>

手动添加方式

iOS ≥ 11.3 可以在 Safari 中打开,点击浏览器底部的分享按钮,选择“添加到主屏幕”

PC 与 Android 的 Chrome 可通过右上角菜单添加(此处以 m.weibo.cn 为例)

PC Android

Service Worker 的更新

这是开发 PWA 应用时需要考虑的一个重要问题

由于 Service Worker 的更新机制(扩展阅读:【Service Worker】生命周期那些事儿),直接单纯的刷新页面并没有结束当前 session,因此依然是旧的 SW 在接管页面,新的 SW 仍旧是 waiting 状态

想要实现在不结束 session 的情况下更新 SW,必须使用 skipWaiting,目前有两种常见的处理方法

注:以下方法中提到的registerServiceWorker.js是由 PWA 插件在src目录中自动生成的,其作用是注册 SW 以及提供其生命周期钩子,具体可以看该 npm 包 register-service-worker

方法一:直接 skipWaiting,并引导用户刷新

这种方法非常暴力且简单,你只需要在步骤2提到的workboxOptions中将skipWaiting设置为true就行了,然后在registerServiceWorker.js中的updated()函数里做一些操作,例如弹出一个对话框来提示用户点击某个按钮以刷新页面

该方法对仅 precache 应用是没有任何影响的

但由于 skipWaiting 后新 SW 会立即接管页面,因此如果你更新了 SW 在处理 runtimeCaching 之类的运行时操作的行为而用户又没有刷新页面,就有可能会出现问题

即除非你能保证同一个页面在两个版本的 SW 相继处理的情况下依然能够正常工作,否则不要使用这个方法

方法二:等待用户同意再 skipWaiting 并刷新

该方法可以解决方法一的局限性,我们可以先弹出一个对话框询问用户是否要更新,用户同意后再 skipWaiting 并刷新

关于这种方法,我只描述大致的思路和做法,因为我没有实际完整地实践过,因为比较复杂麻烦(好的下面我就开始云了)

我们需要在workboxOptions中将skipWaiting设置为false,或者不设置,因为默认值为false

此处,官方文档中提到,当skipWaitingfalse的时候,生成的 SW 会加入以下代码

self.addEventListener('message', (event) => {
    if (event.data && event.data.type === 'SKIP_WAITING') {
        self.skipWaiting();
    }
});

其作用是当 SW 接收到{type:'SKIP_WAITING'}的消息后,SW 就会 skipWaiting

但实际情况是,最终生成的 SW 中并没有这一段代码(也并没有放置在其他 js 中),我猜测这可能是因为加入代码的这一特性是 Workbox 4 才加入的,而插件生成的 SW 引用的是 Workbox 3 的缘故……

对于这个问题有两种可能的解决方法:

  1. workboxPluginMode设置为InjectManifest,然后自己指定一个 SW 里面加上该代码内容
  2. 从谷歌那里下载最新的 Workbox 放置在项目内,并配置workboxOptions中的importWorkboxFromdisable,然后在importScripts中指定本地workbox-sw.js的路径

接着在registerServiceWorker.js中我们可以如下所示在updated()函数中加入一些内容,询问用户是否愿意重载页面以更新应用,若用户同意则向 waiting 状态的 SW 发送{type:'SKIP_WAITING'}消息,并在新 SW 控制页面后立即刷新

updated(reg) {
    // 当控制页面的 SW 改变时刷新
    let refreshing = false;
    navigator.serviceWorker.addEventListener('controllerchange', () => {
        if (refreshing) return;
        window.location.reload();
        refreshing = true;
    });
    // 接下来询问用户是否更新并重载应用
    // 用户同意则执行以下语句
    reg.waiting.postMessage({ type: 'SKIP_WAITING' });
}
搬瓦工VPS优惠套餐,建站稳如狗,支持支付宝,循环出账94折优惠码BWH26FXH3HIQ
年付$28CN2线路,1核/512M内存/10G硬盘/[email protected]点击购买】(经常售罄,请抓紧机会)
年付$47CN2线路,1核/1G内存/20G硬盘/[email protected]点击购买
我的文章对您有帮助吗?
我很可爱 请给我钱
扫一扫拿红包 → 扫商家收款码 → 花呗支付比红包多1分钱的金额
既可免费赞赏,又可完成支付宝支付任务!
Last modification:June 27th, 2019 at 12:37 am
If you think my article is useful to you, please feel free to appreciate

Leave a Comment

7 comments

  1. WeiYuan  Android 9(Android 9) / Google Chrome 74.0.3729.157(Google Chrome 74.0.3729.157)

    是vue好耶!~
    表示我前一个月做了一个在线匿名聊天,硬是看着文档强行上了vue?(用了他的数据绑定,这样自动刷新聊天内容就不用去刷新整个页面了。)第一个版本出来后,就没动过了,js还存在很大问题,部署了一个在腾讯云上。现在觉得自己应该先学Webpack再来深入框架。坐等暑假
    http://94.191.113.229:1080

    1. 神代綺凜  Windows 10 x64 Edition(Windows 10 x64 Edition) / Google Chrome 75.0.3770.100(Google Chrome 75.0.3770.100)
      @WeiYuan

      可以

  2. 一只灰猫  Windows 10 x64 Edition(Windows 10 x64 Edition) / Google Chrome 75.0.3770.100(Google Chrome 75.0.3770.100)

    https://www.pwabuilder.com/ 这里面提供了一些pwa的工具,可以参考。
    ICON那些东西其实可以用 https://realfavicongenerator.net 来完成,包括比较头疼的svg等等。

    1. 神代綺凜  Windows 10 x64 Edition(Windows 10 x64 Edition) / Google Chrome 75.0.3770.100(Google Chrome 75.0.3770.100)
      @一只灰猫

      试了下 svg 生成好像不太近人意,看起来只是单纯的按照轮廓生成了纯色色块

      1. 一只灰猫  Windows 10 x64 Edition(Windows 10 x64 Edition) / Google Chrome 75.0.3770.100(Google Chrome 75.0.3770.100)
        @神代綺凜

        嘛……不用mac,实际效果不清楚,看预览也感觉不太好,算是能用吧。

        另外现在支持pwa的浏览器并不多,safari/chrome之外只有小米百度支持pwa,别的最多只能到sw,ff记得是明确表示不支持,各个厂商分裂到头疼。

        1. 神代綺凜  Windows 10 x64 Edition(Windows 10 x64 Edition) / Google Chrome 75.0.3770.100(Google Chrome 75.0.3770.100)
          @一只灰猫

          发现一个好用的在线svg编辑器 https://editor.method.ac/

    2. 神代綺凜  Windows 10 x64 Edition(Windows 10 x64 Edition) / Google Chrome 75.0.3770.100(Google Chrome 75.0.3770.100)
      @一只灰猫

      这个好