一、背景
大家都知道 Native app 体验确实很好,下载到手机上之后入口也方便。它也有一些缺点:
- 开发成本高(ios 和安卓)
- 软件上线需要审核
- 版本更新需要将新版本上传到不同的应用商店
- 想使用一个 app 就必须去下载才能使用,即使是偶尔需要使用一下下
而 web 网页开发成本低,网站更新时上传最新的资源到服务器即可,用手机带的浏览器打开就可以使用。但是除了体验上比 Native app 还是差一些,还有一些明显的缺点
- 手机桌面入口不够便捷,想要进入一个页面必须要记住它的 url 或者加入书签
- 没网络就没响应,不具备离线能力
- 不像 APP 一样能进行消息推送
那么什么是 PWA 呢?
二、What’s PWA?
PWA 全称 Progressive Web App,即渐进式 WEB 应用。
一个 PWA 应用首先是一个网页, 可以通过 Web 技术编写出一个网页应用. 随后添加上 App Manifest 和 Service Worker 来实现 PWA 的安装和离线等功能
解决了哪些问题?
- 可以添加至主屏幕,点击主屏幕图标可以实现启动动画以及隐藏地址栏
- 实现离线缓存功能,即使用户手机没有网络,依然可以使用一些离线功能
- 实现了消息推送
它解决了上述提到的问题,这些特性将使得 Web 应用渐进式接近原生 App。
三、PWA 的实现
3.1 Manifest 实现添加至主屏幕
index.html
1 | <head> |
manifest.json
1 | { |
Manifest 参考文档:https://developer.mozilla.org/zh-CN/docs/Web/Manifest
可以打开网站 https://developers.google.cn/web/showcase/2015/chrome-dev-summit 查看添加至主屏幕的动图。如果用的是安卓手机,可以下载 chrome 浏览器自己操作看看
3.2 service worker 实现离线缓存
3.2.1 什么是 service worker
Service Worker 是 Chrome 团队提出和力推的一个 WEB API,用于给 web 应用提供高级的可持续的后台处理能力。
Service Workers 就像介于服务器和网页之间的拦截器,能够拦截进出的 HTTP 请求,从而完全控制你的网站。
最主要的特点
- 在页面中注册并安装成功后,运行于浏览器后台,不受页面刷新的影响,可以监听和截拦作用域范围内所有页面的 HTTP 请求。
- 网站必须使用 HTTPS。除了使用本地开发环境调试时(如域名使用 localhost)
- 运行于浏览器后台,可以控制打开的作用域范围下所有的页面请求
- 单独的作用域范围,单独的运行环境和执行线程
- 不能操作页面 DOM。但可以通过事件机制来处理
- 事件驱动型服务线程
为什么要求网站必须是 HTTPS 的,大概是因为 service worker 权限太大能拦截所有页面的请求吧,如果 http 的网站安装 service worker 很容易被攻击
浏览器支持情况
浏览器支持情况详见: https://caniuse.com/#feat=serviceworkers
生命周期
当用户首次导航至 URL 时,服务器会返回响应的网页。
- 第 1 步:当你调用 register() 函数时, Service Worker 开始下载。
- 第 2 步:在注册过程中,浏览器会下载、解析并执行 Service Worker ()。如果在此步骤中出现任何错误,register() 返回的 promise 都会执行 reject 操作,并且 Service Worker 会被废弃。
- 第 3 步:一旦 Service Worker 成功执行了,install 事件就会激活
- 第 4 步:安装完成,Service Worker 便会激活,并控制在其范围内的一切。如果生命周期中的所有事件都成功了,Service Worker 便已准备就绪,随时可以使用了!
chrome://serviceworker-internals 来了解当前浏览器中所有已安装 Service Worker 的详细情况
3.2.2 HTTP 缓存与 service worker 缓存
HTTP 缓存
Web 服务器可以使用 Expires 首部来通知 Web 客户端,它可以使用资源的当前副本,直到指定的“过期时间”。反过来,浏览器可以缓存此资源,并且只有在有效期满后才会再次检查新版本。
使用 HTTP 缓存意味着你要依赖服务器来告诉你何时缓存资源和何时过期。
service worker 缓存
Service Workers 的强大在于它们拦截 HTTP 请求的能力
进入任何传入的 HTTP 请求,并决定想要如何响应。在你的 Service Worker 中,可以编写逻辑来决定想要缓存的资源,以及需要满足什么条件和资源需要缓存多久。一切尽归你掌控!
3.2.3 实现离线缓存
index.html
1 | <!DOCTYPE html> |
注:Service Worker 的注册路径决定了其 scope 默认作用页面的范围。
如果 service-worker.js 是在 /sw/ 页面路径下,这使得该 Service Worker 默认只会收到 页面/sw/ 路径下的 fetch 事件。
如果存放在网站的根路径下,则将会收到该网站的所有 fetch 事件。
如果希望改变它的作用域,可在第二个参数设置 scope 范围。示例中将其改为了根目录,即对整个站点生效。
service-worker.js
1 | var cacheName = 'helloWorld'; // 缓存的名称 |
注:为什么用 request.clone()和 response.clone()
需要这么做是因为 request 和 response 是一个流,它只能消耗一次。因为我们已经通过缓存消耗了一次,然后发起 HTTP 请求还要再消耗一次,所以我们需要在此时克隆请求
Clone the request—a request is a stream and can only be consumed once.
3.2.4 调试相关
chrome 浏览器打开 https://googlechrome.github.io/samples/service-worker/basic/index.html ,这是一个实现了 service worker 离线缓存功能的网站,打开调试工具
介绍一个图中的 1.和 2.
- 勾选可以模拟网站离线情况,勾选后 network 会有一个黄色警告图标,该网站已经离线。此时刷新页面,页面仍然能够正常显示
- 当前 service worker 的 scope。它能够拦截 https://googlechrome.github.i…,同样也能够拦截https://googlechrome.github.i...下的请求
调试面板具体代表的什么参看 https://x5.tencent.com/tbs/guide/serviceworker.html 的第三部分
3.3 service worker 实现消息推送
- 步骤一、提示用户并获得他们的订阅详细信息
- 步骤二、将这些详细信息保存在服务器上
- 步骤三、在需要时发送任何消息
不同浏览器需要用不同的推送消息服务器。以 Chrome 上使用 Google Cloud Messaging
作为推送服务为例,第一步是注册 applicationServerKey(通过 GCM 注册获取),并在页面上进行订阅或发起订阅。每一个会话会有一个独立的端点(endpoint),订阅对象的属性(PushSubscription.endpoint) 即为端点值。将端点发送给服务器后,服务器用这一值来发送消息给会话的激活的 Service Worker (通过 GCM 与浏览器客户端沟通)。
步骤一和步骤二
index.html
1 | <!DOCTYPE html> |
步骤三 服务器发送消息给 service worker
app.js
1 | const webpush = require('web-push'); |
service worker 监听 push 事件,将通知详情推送给用户
service-worker.js
1 | self.addEventListener('push', function (event) { |
扩展知识:service worker 的更新
总结
PWA 的优势
- 可以将 app 的快捷方式放置到桌面上,全屏运行,与原生 app 无异
- 能够在各种网络环境下使用,包括网络差和断网条件下,不会显示 undefined
- 推送消息的能力
- 其本质是一个网页,没有原生 app 的各种启动条件,快速响应用户指令
PWA 存在的问题
- 支持率不高:现在 ios 手机端不支持 pwa,IE 也暂时不支持
- Chrome 在中国桌面版占有率还是不错的,安卓移动端上的占有率却很低
- 各大厂商还未明确支持 pwa
- 依赖的 GCM 服务在国内无法使用
- 微信小程序的竞争
尽管有上述的一些缺点,PWA 技术仍然有很多可以使用的点。
- service worker 技术实现离线缓存,可以将一些不经常更改的静态文件放到缓存中,提升用户体验。
- service worker 实现消息推送,使用浏览器推送功能,吸引用户
- 渐进式开发,尽管一些浏览器暂时不支持,可以利用上述技术给使用支持浏览器的用户带来更好的体验。