渐进式网页应用(PWA) - 图1

简介

全名 progressive web app,译为 渐进式网页应用,由谷歌推出。
微信的小程序某种程度上就是微信世界的 PWA,只不过 Google 的野心更大。
PWA 不是特指某一项技术,而是应用了多项技术的 Web App

背景

WebApp 中,我们的应用通常由两部分组成。
一部分是属于 主进程,包括 JS 同步/异步 程序的执行 、DOM 渲染 等,主进程一个时间点,只能执行一项任务。
另一部分属于 Worker进程,它重新在后台起了一个进程,它和应用的主进程互不影响,可以同时执行。常见的worker有,web worker, service worker, shared worker等等。

发展

2014年 W3C公布 Service Worker 相关草案
2015年 Service Worker 在生产环境被 Chrome 支持
PWA 诞生(如果把 PWA 的关键技术之一 Service Worker 的出现作为标志事件)
2015年以后 PWA 相关的技术不断升级优化,在用户体验和用户留存两方面都提供了好的解决方案

对比

原生应用 PWA 应用
差异性
- 使用原生SDK和开发工具开发
- 需要考虑跨平台,不同系统往往需要独立开发
- 需要发布到应用商店才能下载使用
- 由于天生封闭的基因,内容无法被索引

- 使用HTML,CSS,JS开发
- 无需考虑跨平台,只需要考虑浏览器兼容性
- 通过url访问,无需发布到应用商店
- 内容能够被搜索引擎索引
共同性
- 可以安装到手机主屏,生成应用图标
- 直接运行于操作系统上,访问系统资源方便
- 可以离线使用
- 可以获取消息通知
总结 可以发现PWA具备了原生应用的主要能力,但是开发流程却比原生应用更加简洁:
- html/css/js的群众基础更好,开发效率更高
- 省去了为不同系统开发独立版本的大量成本
- 省去了上架到应用市场的繁琐流程
- 无需前往应用商店下载,用户使用起来也更加方便

同时值得注意的是:
PWA还是相对比较新的技术,实现规范还有很多调整的空间,部分浏览器对PWA的支持也还不完善,但是PWA是一个趋势,值得花时间学习。 | |

特点

渐进式

适用于所有浏览器,因为它是以渐进式增强作为宗旨开发的。
Progressive 是指 PWA 的构建过程。构成 PWA 的标准都来自 Web 技术, 它们都是浏览器提供的、向下兼容的、没有额外运行时代价的技术。 因此可以把任何现有的框架开发的 Web 页面改造成 PWA,而且与 SPA 方案不同, 没有强组件化机制,因此不需要一把重构可以逐步地迁移和改善。

自适应

适合任何机型,桌面设备、移动设备、平板电脑以及任何未来的设备

连接无关性

能够借助 Service Worker 在离线或者网络较差的情况下正常访问

类似应用

由于是在 App Shell 模型基础上开发,因为应具有的交互和导航,给用户应用般的熟悉感

持续更新

在服务工作线程更新进程的作用下时刻保持最新状态

安全

通过 HTTPS 协议提供服务,防止窥探和确保内容不被篡改

粘性

通过推送离线通知等,可以让用户回流

可索引

W3C应用清单文件和 Service Worker 可以让搜索引擎索引到,从而将其识别为“应用”

可安装

用户可以添加常用的 webapp 到桌面,免去去应用商店下载的麻烦

可链接

通过链接即可分享内容,无需下载安装

注意

安全性事项

PWA应用需要在本地 localhost:8080 上运行以及服务端必须是在 https 协议下, 要保证你的页面是安全页面。

组成

其核心技术包括 Web App ManifestService WorkerWeb Push,等等。 渐进式网页应用(PWA) - 图2

Web APP Manifest

语雀内容

Service Worker

语雀内容

Web Push

语雀内容

操作

检查页面是否用到了PWA

开发者如何了解一个网页是否具备了 PWA 的一些特点呢?
在 Chrome 浏览器的开发者工具的 Audit 功能选项卡可以帮助我们进行检测。
检测后会有一个小报告,你可以根据这份 cheklist 来进一步优化你的 PWA 应用。
image.png

应用

在Vue-cli中使用PWA

安装插件

在 Vue-cli3 及以上版本的脚手架中,只需要用 vue 的添加插件指令即可完成安装。

  1. vue add pwa

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

该插件加入的 service worker 只会在生产环境下 (即只在运行 npm run buildyarn build 时) 开启。 在开发环境下开启 service worker 并不推荐,因为它会导致之前的缓存资源被使用而未包含最新的本地改变。

检查变动

安装后,Vue 脚手架会对已有项目做了如下变动:

  • 添加依赖 register-service-worker@vue/cli-plugin-pwa (开发模式下)
  • 在 src 目录下创建了一个 registerServiceWorker.js 文件,负责注册service work
  • 在 src/main.js 中引入 registerServiceWorker.js 文件
  • 在 vue.config.js 文件中添加 pwa 对象
  • 在 public/img/icons 文件中添加预置各种尺寸的图标文件(默认是Vue的标识)

注意将相关的文件添加到 Git 版本控制中。
image.png

处理兼容性

目前部分浏览器还不支持service work,我们需要保证它们依旧能正常工作。
registerServiceWorker.js 文件目前没有进行 service work 可用性的判定,我们需要手动修改。
主要通过 'serviceWorker' in window.navigator 来判断,修改后代码如下:

  1. /* eslint-disable no-console */
  2. import { register } from 'register-service-worker'
  3. if ('serviceWorker' in window.navigator && process.env.NODE_ENV === 'production') {
  4. register(`${process.env.BASE_URL}service-worker.js`, {
  5. ready() {
  6. console.log(
  7. 'App is being served from cache by a service worker.\n' +
  8. 'For more details, visit https://goo.gl/AFskqB'
  9. )
  10. },
  11. registered() {
  12. console.log('Service worker has been registered.')
  13. },
  14. cached() {
  15. console.log('Content has been cached for offline use.')
  16. },
  17. updatefound() {
  18. console.log('New content is downloading.')
  19. },
  20. updated() {
  21. console.log('New content is available; please refresh.')
  22. },
  23. offline() {
  24. console.log('No internet connection found. App is running in offline mode.')
  25. },
  26. error(error) {
  27. console.error('Error during service worker registration:', error)
  28. }
  29. })
  30. }

编写workbox配置

该插件基于 workbox 实现,我们需要在 src 下创建 service-worker.js 文件并根据实际需求编写配置。

  1. /* eslint-disable no-undef*/
  2. if (workbox) {
  3. console.log(`Yay! Workbox is loaded 🎉`)
  4. } else {
  5. console.log(`Boo! Workbox didn't load 😬`)
  6. }
  7. workbox.core.setCacheNameDetails({
  8. prefix: "ochase-search",
  9. suffix: "v1.0.0"
  10. })
  11. workbox.core.skipWaiting() // 强制等待中的 Service Worker 被激活
  12. workbox.core.clientsClaim() // Service Worker 被激活后使其立即获得页面控制权
  13. workbox.precaching.precacheAndRoute(self.__precacheManifest || []) // 设置预加载
  14. // 缓存web的css资源
  15. workbox.routing.registerRoute(
  16. // Cache CSS files
  17. /.*\.css/,
  18. // 使用缓存,但尽快在后台更新
  19. new workbox.strategies.StaleWhileRevalidate({
  20. // 使用自定义缓存名称
  21. cacheName: "css-cache"
  22. })
  23. )
  24. // 缓存web的js资源
  25. workbox.routing.registerRoute(
  26. // 缓存JS文件
  27. /.*\.js/,
  28. // 使用缓存,但尽快在后台更新
  29. new workbox.strategies.StaleWhileRevalidate({
  30. // 使用自定义缓存名称
  31. cacheName: "js-cache"
  32. })
  33. )
  34. // 缓存web的图片资源
  35. workbox.routing.registerRoute(
  36. /\.(?:png|gif|jpg|jpeg|svg)$/,
  37. new workbox.strategies.StaleWhileRevalidate({
  38. cacheName: "images",
  39. plugins: [
  40. new workbox.expiration.ExpirationPlugin({
  41. maxEntries: 60,
  42. maxAgeSeconds: 30 * 24 * 60 * 60 // 设置缓存有效期为30天
  43. })
  44. ]
  45. })
  46. )
  47. // 如果有资源在其他域名上,比如cdn、oss等,这里做单独处理,需要支持跨域
  48. workbox.routing.registerRoute(
  49. /^https:\/\/cdn\.ochase\.com\/.*\.(jpe?g|png|gif|svg)/,
  50. new workbox.strategies.StaleWhileRevalidate({
  51. cacheName: "cdn-images",
  52. plugins: [
  53. new workbox.expiration.ExpirationPlugin({
  54. maxEntries: 60,
  55. maxAgeSeconds: 5 * 24 * 60 * 60 // 设置缓存有效期为5天
  56. })
  57. ],
  58. fetchOptions: {
  59. credentials: "include" // 支持跨域
  60. }
  61. })
  62. )

配置pwa对象

既可以在 vue.config.js 文件中编辑 pwa 对象进行配置,也可以在 package.json 文件中进行配置。
你也许已经注意到——在 @vue/cli-plugin-pwa 插件安装后并没有默认在项目中为我们创建 manifest.json 文件。
因为在Vue-cli3之后的版本插件会在项目编译时根据 pwa 配置对象中的设定,自动为我们创建该文件。
这样一来为Vue PWA的配置管理提供了更为一致的接口。

  • pwa.workboxPluginMode

这个选项允许你在底层的 workbox-webpack-plugin 所支持的两种模式之间进行选择。
'GenerateSW' (默认值),每次重新构建你的 web app 时都会创建一个新的 service worker 文件。
'InjectManifest' 允许你以一个现成的 service worker 文件开始,然后创建一份文件拷贝,并把“precache manifest”注入其中。(自定义编辑模式)

  • pwa.workboxOptions

这些选项会传入底层的 workbox-webpack-plugin
当 workbox 模式设置为 InjectManifest 时的配置选项从这里查看 [ 传送门 ]
当 workbox 模式设置为 GenerateSW 时的配置选项从这里查看 [ 传送门 ]

  • pwa.name

默认值:package.json 的 “name” 字段。

  • pwa.themeColor

默认值:'#4DBA87'

  • pwa.msTileColor

默认值:'#4DBA87'
**

  • pwa.appleMobileWebAppCapable

这里的默认值是 'no' 因为 iOS 11.3 之前都不支持 PWA。

  • pwa.appleMobileWebAppStatusBarStyle

默认值:'default'

  • pwa.assetsVersion

默认值:''。该选项会为图标和 manifest 文件的 URL 添加 ?v=<pwa.assetsVersion>。以便在需要的时候应对浏览器缓存。

  • pwa.manifestPath

默认值:'manifest.json'。应用的 manifest 文件路径。

  • pwa.iconPaths

改变这些值可以为图标设置不同的路径。

  1. {
  2. favicon32: 'img/icons/favicon-32x32.png',
  3. favicon16: 'img/icons/favicon-16x16.png',
  4. appleTouchIcon: 'img/icons/apple-touch-icon-152x152.png',
  5. maskIcon: 'img/icons/safari-pinned-tab.svg',
  6. msTileImage: 'img/icons/msapplication-icon-144x144.png'
  7. }

具体的配置参考官方文档。 [ 传送门 ]

  1. // Inside vue.config.js
  2. module.exports = {
  3. // ...其它 vue-cli 插件选项...
  4. pwa: {
  5. name: 'My App',
  6. themeColor: '#4DBA87',
  7. msTileColor: '#000000',
  8. appleMobileWebAppCapable: 'yes',
  9. appleMobileWebAppStatusBarStyle: 'black',
  10. // 配置 workbox 插件
  11. workboxPluginMode: 'InjectManifest',
  12. workboxOptions: {
  13. // InjectManifest 模式下 swSrc 是必填的。
  14. swSrc: './src/service-worker.js',
  15. importWorkboxFrom: 'local'
  16. // ...其它 Workbox 选项...
  17. }
  18. }
  19. }

importWorkboxFrom 这个选项很重要:
值为 local 时将会在自定义 service worker 文件中引入 workbox 本地类库,反之会使用 Google 提供的 cdn(国内不友好)。
值为 disabled 时将不会在自定义 service worker 文件中引入 workbox 类库。
当前(2020-07-26) vue-cli-pwa 插件中使用的是 V4 版本的 workbox,到了 V5 版本 importWorkboxFrom 属性便不再受支持,届时需要查阅最新文档。

在Nuxt中使用PWA

[ 传送门 ]

参考链接 https://juejin.im/post/5a9e8ad5f265da23a40456d4 https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli-plugin-pwa/README.md https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API https://juejin.im/post/5a6c86e451882573505174e7