阅读 5541

Vue+PWA实践过程

前言


最近这一年,作为一名前端狗,一直在优化性能的道路一去不回头。vue、nginx、webpack、redis,能做优化的地方,都不曾放过,只为那最后一秒的舒爽!(猥琐的一笑)。

之前一直对PWA所有关注,希望通过PWA对目前的项目做一次性能上的提升,趁着最近还算有点时间,对现在公司使用的前端结构做一次改造!

本次改造主要分两部分:

  • 改造基于vue cli3创建的项目
  • 改造基于webpack3.0打包的项目

关于PWA和Service Work的相关知识,在这里就不过多介绍了,大家可以自行百度

改造基于vue cli3创建的项目


新项目直接在vue create的时候,选择PWA模板,就可以启用PWA,这里主要介绍如果在已有项目中启用PWA

1. 安装PWA插件

vue add pwa
复制代码

需要注意的是vue cli3 是基于workbox来实现PWA

2.目录结构

安装完插件后,目录结构如下:

  • registerServiceWorker.js:负责注册service work
  • service-worker.js:自定义的service work配置文件

3.修改registerServiceWorker.js,做兼容处理

目前部分浏览器还不支持service work,我们需要保证它们依旧能正常工作。 通过'serviceWorker' in window.navigator来判断浏览器是否支持sw,具体代码如下:

/* eslint-disable no-console */

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

if ('serviceWorker' in window.navigator && process.env.NODE_ENV === 'production') {
  register(`${process.env.BASE_URL}service-worker.js`, {
    ready() {
      console.log(
        'App is being served from cache by a service worker.\n' +
        'For more details, visit https://goo.gl/AFskqB'
      );
    },
    registered() {
      console.log('Service worker has been registered.');
    },
    cached() {
      console.log('Content has been cached for offline use.');
    },
    updatefound() {
      console.log('New content is downloading.');
    },
    updated() {
      console.log('New content is available; please refresh.');
    },
    offline() {
      console.log('No internet connection found. App is running in offline mode.');
    },
    error(error) {
      console.error('Error during service worker registration:', error);
    }
  });
}

复制代码

4.编写service-work.js

完整的service-work,可以参考我们的前端模板工程中service-work.js,下面我们来逐步介绍:

  • 设置缓存的前缀和后缀,我们一般用项目名作为前缀,版本号作为后缀
workbox.core.setCacheNameDetails({
  prefix: 'easy-front-vue-cli3',
  suffix: 'v1.0.0'
});
复制代码
  • 配置service work的更新激活策略
workbox.core.skipWaiting(); // 强制等待中的 Service Worker 被激活
workbox.core.clientsClaim(); // Service Worker 被激活后使其立即获得页面控制权
复制代码
  • 设置预加载
workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
复制代码
  • 设置资源缓存策略 workbox主要提供了以下几种缓存策略:

Stale-While-Revalidate:使用缓存的内容(如果可用)尽快响应请求,如果未缓存,则使用网络请求。 然后,网络请求得到数据用于更新缓存。

Cache First:如果请求命中缓存,则将使用缓存的响应来完成请求,不会使用网络。 如果没有命中缓存,将通过网络请求来获取数据,并且将数据缓存,以便下次直接从缓存获取数据。该模式可以为前端提供快速响应的同时,减轻服务器压力。但是数据的时效性就需要开发者通过设置缓存过期时间或更改service-work.js里面的修订标识来完成缓存文件的更新

Network First:优先从网络获取最新数据。 如果成功,它会将数据放入缓存。如果网络无法返回响应,则将使用缓存数据。

Network Only:只使用网络请求数据

Cache Only:只使用缓存数据

具体使用什么策略,还是需要根据实际的业务场景和需求来设定,我采用的基本策略如下:

// 缓存web的css资源
workbox.routing.registerRoute(
  // Cache CSS files
  /.*\.css/,
  // 使用缓存,但尽快在后台更新
  workbox.strategies.staleWhileRevalidate({
    // 使用自定义缓存名称
    cacheName: 'css-cache'
  })
);

// 缓存web的js资源
workbox.routing.registerRoute(
  // 缓存JS文件
  /.*\.js/,
  // 使用缓存,但尽快在后台更新
  workbox.strategies.staleWhileRevalidate({
    // 使用自定义缓存名称
    cacheName: 'js-cache'
  })
);

// 缓存web的图片资源
workbox.routing.registerRoute(
  /\.(?:png|gif|jpg|jpeg|svg)$/,
  workbox.strategies.staleWhileRevalidate({
    cacheName: 'images',
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 60,
        maxAgeSeconds: 30 * 24 * 60 * 60 // 设置缓存有效期为30天
      })
    ]
  })
);

// 我们很多资源在其他域名上,比如cdn、oss等,这里做单独处理,需要支持跨域
workbox.routing.registerRoute(
  /^https:\/\/cdn\.my\.com\/.*\.(jpe?g|png|gif|svg)/,
  workbox.strategies.staleWhileRevalidate({
    cacheName: 'cdn-images',
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 60,
        maxAgeSeconds: 5 * 24 * 60 * 60 // 设置缓存有效期为5天
      })
    ],
    fetchOptions: {
      credentials: 'include' // 支持跨域
    }
  })
);

// 缓存get api请求的数据
workbox.routing.registerRoute(
  new RegExp('https://m.my.com/api'),
  workbox.strategies.networkFirst({
    cacheName: 'api'
  })
);

// 缓存post api请求的数据
// const bgSyncPlugin = new workbox.backgroundSync.Plugin('apiQueue', {
//   maxRetentionTime: 1 * 60
// });

// workbox.routing.registerRoute(
//   /.*\/api\/.*/,
//   new workbox.strategies.NetworkOnly({
//     plugins: [bgSyncPlugin]
//   }),
//   'POST'
// );

复制代码

5.修改main.js

加入以下代码

import './registerServiceWorker';
复制代码

6.修改vue.config.js

参考我们的前端模板工程中vue.config.js.js,下面列举了跟PWA相关的部分:

pwa: {
    name: 'easy-front-vue-cli3',
    themeColor: '#4DBA87',
    msTileColor: '#000000',
    appleMobileWebAppCapable: 'yes',
    appleMobileWebAppStatusBarStyle: 'black',
    // configure the workbox plugin (GenerateSW or InjectManifest)
    workboxPluginMode: 'InjectManifest',
    workboxOptions: {
      // swSrc is required in InjectManifest mode.
      swSrc: 'src/service-worker.js',
      importWorkboxFrom: 'disabled',
      importScripts: 'https://cdn.your.info/workbox-v4.3.1/workbox-sw.js'
      // ...other Workbox options...
    }
  }
复制代码
  • workboxPluginMode:workbox的模式,GenerateSW使用默认模式,InjectManifest使用自定义模式,我推荐使用InjectManifest,自由度更大
  • workboxOptions.swSrc: 指定service-worker.js所在位置
  • workboxOptions.importWorkboxFrom: 引入workbox依赖库的方式,默认的依赖库在Google的cdn上,国内情况大家都懂,因此建议将依赖库放到大家自己的cdn上,所以我这里设置了disabled
  • workboxOptions.importScripts: 指定你自己的依赖库的cdn的url

7.配置manifest.json

PWA支持创建快捷方式,manifest.json包含了快捷方式的名称、图标、入口路径等配置。 如果不需要快捷方式,也可以不创建

{
  "name": "easy-front-vue-cli3",
  "short_name": "easy-front-vue-cli3",
  "icons": [
    {
      "src": "/img/icons/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/img/icons/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start_url": "/",
  "display": "standalone",
  "background_color": "#000000",
  "theme_color": "#4DBA87"
}
复制代码

改造基于webpack3.0打包的项目

我们这里只介绍跟vue cli3不同的部分,registerServiceWorker.js、service-work.js和manifest.json这些相同部分就不重复介绍了。

1.安装依赖包

npm install workbox-webpack-plugin -D
npm install register-service-worker -S
复制代码

2.修改webpack.prod.conf.js

// 在webpack中加入workbox插件,并进行配置
const {InjectManifest} = require('workbox-webpack-plugin');
  webpackConfig.plugins.push(new InjectManifest({
    swSrc: 'src/service-worker.js',
    importWorkboxFrom: 'disabled',
    importScripts: 'https://cdn.your.info/workbox-v4.3.1/workbox-sw.js'
  }));
复制代码

3.修改main.js

加入以下代码

import './registerServiceWorker';
复制代码

参考


我的模板工程:easy-front-vue-cli3

workbox仓库地址:workbox

PWA学习资料:PWA应用实战