PWA详解及实践探索
PWA 不是一项具体的技术,他是应用了一系列技术进行使用体验优化后的Web APP,具有与Native App 一致的用户体验,能够添加主屏图标、离线可用、接收离线通知等。
什么是PWA
PWA 全称Progressive Web Apps(渐进式Web应用程序),旨在使用现有的web技术提供用户更优的使用体验。 PWA 具有三个主要的特性
- 可靠 一方面是指 PWA 的安全性,PWA 只能运行在 HTTPS 上;另一方面是指在网络不稳定或者没网情况下,PWA 依然可以访问。
- 快速 快速响应用户的交互行为,并且具有平滑流畅的动画、加载速度、渲染速度和渲染性能等。
- 用户粘性 通过添加到桌面以及离线消息推送,能带来用户的第二次访问,并且依靠良好的用户体验吸引用户再次访问。 PWA背后不是一种新的技术,而是集合当前多种web技术的一种集合。分别利用各自的功能来完成渐进式的整体需求
下面是几个demo
http://boscdn.bpc.baidu.com/assets/Screenrecorder-2018-03-20-21-54-22-56-2a6d5ac3.mp4 http://boscdn.bpc.baidu.com/assets/Screenrecorder-2018-03-21-17-27-31-297-a4d9412e.mp4 https://pwa.rocks
Web App Manifest
Web App Manifest 是支持站点在主屏上创建图标的技术方案,并且定制 PWA 的启动画面的图标和颜色等,如下图:
就是一个外链的 json 文件,通过 link 来引入:<link rel=“manifest” href=“/assets/manifest.json”>,文件的具体内容如图,Web App Manifest 的标准里还有很多其他的字段可以去 W3C 的标准里查找 当 Web 添加到桌面后,会出现的 icon 和 name。name 就是用来定义桌面上显示的名字,不过,如果你的名字取的太长,大于 icon 的宽度,可能会造成显示上的问题。所以,这里还有另外一个 short_name 用来当宽度不够时,显示的精简版字体。
icons: {Array.<ImageObject>} 应用图标列表
其中 ImageObject 的属性值包括:
- src: {string} 图标 url
- type {string=} 图标的 mime 类型,非必填项,该字段可让浏览器快速忽略掉不支持的图标类型
- sizes {string} 图标尺寸,格式为widthxheight,宽高数值以 css 的 px 为单位。如果需要填写多个尺寸,则使用空格进行间隔,如"48x48 96x96 128x128“
Display
定义 web 在打开时使用哪种预览模式。这里主要有 4 种:
- fullscreen:全屏。所有可用的区域都可以被排列,目前该特性还未被完全支持。
- standalone:当做一个独立应用。该模式是忽略掉浏览器本身的一些导航栏。不过,它可以进行下拉刷新。
- minimal-ui:该模式的外观和 standalone 差不多,不过它会提供用来控制 navigator 的最小 UI。不支持
- browser:使用默认浏览器模式。
就是一个外链的 json 文件,通过 link 来引入:<link rel=“manifest” href=“/assets/manifest.json”>,文件的具体内容如图,Web App Manifest 的标准里还有很多其他的字段可以去 W3C 的标准里查找
当 Web 添加到桌面后,会出现的 icon 和 name。name 就是用来定义桌面上显示的名字,不过,如果你的名字取的太长,大于 icon 的宽度,可能会造成显示上的问题。所以,这里还有另外一个 short_name 用来当宽度不够时,显示的精简版字体。
Service Worker
Web Worker这个 API 的唯一目的就是解放主线程,Web Worker 是脱离在主线程之外的,将一些复杂的耗时的活交给它干,完成后通过 postMessage 方法告诉主线程,而主线程通过 onMessage 方法得到 Web Worker 的结果反馈。
- Service Worker 在 Web Worker 的基础上加上了持久离线缓存能力
- 一旦被 install,就永远存在,除非被手动 unregister
- 用到的时候可以直接唤醒,不用的时候自动睡眠
- 可编程拦截代理请求和返回,缓存文件,缓存的文件可以被网页进程取到(包括网络离线状态)
- 离线内容开发者可控
- 能向客户端推送消息
基于浏览器中的 javaScript 单线程的现实逐渐不能满足现代web需求的现状,例如耗时的计算,用户的交互显然会受影响。- 为了将这些耗时操作从主线程中解放出来,早期W3C新增了一个Web Worker 的 API,可以脱离主线程单独执行,并且可以与主线程交互。
- 不过Web Worker是临时性的依赖于创建页面 ,不能满足我们持久化的需求。
- 冲着这个目标,下面就比较容易解决了,搞个能持久存在的就行了。
- 在Web Worker的基础上,W3C新增了service worker来满足我们持久化的需求。
- 其生命周期与页面无关,关联页面未关闭时,它也可以退出,没有关联页面时,它也可以启动功能
Service Worker 的缓存机制是依赖 Cache API 实现的
Chrome搞了个Service Worker出来,给了Web一个可以跑在后台的线程,它可以搭配非常靠谱的Cache API做缓存、可以拦截所有HTTP请求并使用Fetch API进行response,一个非常完备的Proxy就这么诞生了。
Service Worker 的工作原理是基于注册、安装、激活等步骤在浏览器 js 主线程中独立分担缓存任务的
要安装 Service Worker, 我们需要通过在 js 主线程(常规的页面里的 js )注册 Service Worker 来启动安装,这个过程将会通知浏览器我们的 Service Worker 线程的 javaScript 文件在什么地方呆着。
这段代码首先是要判断 Service Worker API 的可用情况,支持的话咱们才继续谈实现,否则免谈了。
如果支持的话,在页面 onload 的时候注册位于 /sw.js 的 Service Worker 每次页面加载成功后,就会调用 register() 方法,浏览器将会判断 Service Worker 线程是否已注册并做出相应的处理。
register 方法的 scope 参数是可选的,用于指定你想让 Service Worker 控制的内容的子目录。本 demo 中服务工作线程文件位于根网域, 这意味着服务工作线程的作用域将是整个来源。
Service Worker 线程将接收 scope 指定网域目录上所有事项的 fetch 事件,如果我们的 Service Worker 的 javaScript 文件在 /a/b/sw.js, 不传 scope 值的情况下, scope 的值就是 /a/b
install 事件我们会绑定在 Service Worker 文件中,在 Service Worker 安装成功后,install 事件被触发。
install 事件一般是被用来填充你的浏览器的离线缓存能力。为了达成这个目的,我们使用了 Service Worker 新的标志性的存储 cache API — 一个 Service Worker 上的全局对象,它使我们可以存储网络响应发来的资源,并且根据它们的请求来生成key。这个 API 和浏览器的标准的缓存工作原理很相似,但是是只对应你的站点的域的。它会一直持久存在,直到你告诉它不再存储,你拥有全部的控制权。localStorage 的用法和 Service Worker cache 的用法很相似,但是由于 localStorage 是同步的用法
ExtendableEvent.waitUntil() 方法——这会确保 Service Worker 不会在 waitUntil() 里面的代码执行完毕之前安装完成 使用了 caches.open() 方法来创建了一个叫做 v1 的新的缓存,将会是我们的站点资源缓存的第一个版本。它返回了一个创建缓存的 promise,当它 resolved 的时候,我们接着会调用在创建的缓存实例(Cache API)上的一个方法 addAll(),这个方法的参数是一个由一组相对于 origin 的 URL 组成的数组,这些 URL 就是你想缓存的资源的列表。
在 activate 事件发生时,通过执行 self.clients.claim() 方法,更新所有客户端上的 Service
Service Worker 的特殊之处除了由浏览器触发更新之外,还应用了特殊的缓存策略: 如果该文件已 24 小时没有更新,当 Update 触发时会强制更新。这意味着最坏情况下 Service Worker 会每天更新一次。
每次任何被 Service Worker 控制的资源被请求到时,都会触发 fetch 事件,这些资源包括了指定的 scope 内的 html 文档,和这些 html 文档内引用的其他任何资源(比如 index.html 发起了一个跨域的请求来嵌入一个图片,这个也会通过 Service Worker)
废弃状态 ( redundant ):这个状态表示一个 Service Worker 的生命周期结束 进入废弃 (redundant) 状态的原因可能为这几种: 安装 (install) 失败 激活 (activating) 失败 新版本的 Service Worker 替换了它并成为激活状态
Push Notification( 消息通知 )
- Push 和 Notification 是两个不同的功能,涉及到两个 API 。
- Notification 是浏览器发出的通知消息。需要用户的同意:Notification.requestPermission();
- Push 和 Notification 的关系,Push:服务器端将更新的信息传递给 SW ,Notification: SW 将更新的信息推送给用户。
Notification 也不是一开始就具备的,这也需要用户的同意才行:模拟像 APP 推送,能够打开 APP 这种行为,在 SW 中监听 notificationclick 即可
Web Push
首先要生成 pair keys,接着通过客户端订阅,然后才能发送其中的 key 是我们之前用 web-push 生成的。我们通过 subscribe() 会自动得到本次订阅独一无二的描述,当然,我们之前需要检查一下订阅状态,如果已经订阅了,就没必要重复订阅了
后台也需要存储该次订阅的信息。后台的话,发送 push 给前台。之后,我们在 sw.js 中的 push 事件中做相关处理即可,不过,你能不能成功还是要看运气的。因为,我们一直活在墙中,如果你使用 Chrome 的话。它的 message server 你是连不上的。 所以,关于 push 还需要等国内的浏览器跟上才行。
PWA 的兼容性
PWA 在 2017 年初,仅仅 Chrome 和 Firefox 支持 PWA,经过一年的发展,国内主流浏览器都已经支持 PWA,iOS 在 新发布的11.3 版本中也支持了 PWA。
基于 Vue.js 的 PWA 解决方案
Lavas:https://github.com/lavas-project/lavas
开发桌面端应用
Nw.js: https://nwjs.io/ Electron: https://electron.org.cn/doc/index.html