PWA--下一代的web应用模型

PWA--下一代的web应用模型

概述

不知道从什么时候开始,当我们在浏览网页时,页面会出现一个弹窗,询问我们是否要将此网页保存到本地,如果我们选择了确定,在设备的主屏上就会出现一个新的 app,而它就是我们刚才浏览的网页。这种 app 我们并没有从应用商店去下载,但是我们可以像使用 app 一样去使用它,也可以像卸载其他 app 一样去将它卸载,那么这究竟是个什么东西呢,它就是这篇文章的主角——PWA。

什么是 PWA

PWA 是 Progressive Web App 的简写,中文名称为渐进式 web 应用。它是 Google 在 2016 年提出的概念,2017 年落地的 web 技术,一种在移动端利用提供的标准化框架,在网页应用中实现和原生应用相近的用户体验的渐进式网页应用。PWA 不单指一种技术,你也可以将其理解为一种思想和概念,目的就是对标原生 app,将 Web 网站通过一系列的 Web 技术去优化它,提升其安全性,性能,流畅性,用户体验等各方面指标,最后达到用户就像在用 app 一样的感觉。

PWA 为什么会出现

说到 app,我们就不得不提起 native app 即原生 app,现在非常主流的移动端应用。原生 app 使用起来会很流畅,性能好,安全性也可以很高,这是它很显著的优势。但是缺点呢,也很明显,比如:

  1. 开发成本很昂贵
  2. 软件上线,版本更新都需要发布到不同的商店,并通过审核

这对开发人员来说是比较麻烦的事情。
而对于用户来说,有些 APP 可能使用频率特别少,但还是不得不去商店中下载庞大的安装包,或者可能一段时间不使用以后,随着版本的更新,也不得不去重新更新并安装。

PWA 则完美地避免了这些问题。

如何判断一个 web 应用是 PWA

要判断一个 web 应用是否是 PWA,可以从以下几个方面去考虑:

  1. 可发现的—— 内容可以通过搜索引擎发现。
  2. 可安装—— 可以出现在设备的主屏幕。
  3. 可链接—— 你可以简单地通过一个 URL 来分享它。
  4. 独立于网络——它可以在离线状态或者是在网速很差的情况下运行。
  5. 渐进式—— 它在老版本的浏览器仍旧可以使用,在新版本的浏览器上可以使用全部功能。
  6. 可重用—— 无论何时有新的内容它都可以发送通知。
  7. 响应性—— 它在任何具有屏幕和浏览器的设备上可以正常使用——包括手机,平板电脑,笔记本,电视,冰箱,等。
  8. 安全—— 在你和应用之间的连接是安全的,可以阻止第三方访问你的敏感数据。

当然,在安装方式上 PWA 应用与原生 app 有很大的不同,但是在实际使用上,与原生应用的差距非常小,对于用户来说,几乎是无感的。

核心功能

Service-Workers

Service Workers 是浏览器和网络之间的虚拟代理,运行在一个与页面的 JavaScript 主线程独立的线程上,并且没有对 DOM 结构的任何访问权限,并且可以在不同的上下文之间发送和接收信息。 您可分配给 Service Worker 一些任务,并使用基于 Promise 的方法在任务完成时收到结果。他们不仅仅提供离线功能,还提供包括处理通知,在单独的线程上执行繁重的计算等。Service workers 非常强大,因为他们可以控制网络请求,修改网络请求,返回缓存的自定义响应,或合成响应。因为它们非常强大,所以 Service Workers 只能在安全的上下文中执行(即 HTTPS )。

注册 service worker

1
2
3
4
5
6
7
8
9
10
11
12
if ("serviceWorker" in navigator) {
// 浏览器支持SW
// Service Worker 的注册路径决定了其 scope 默认作用页面的范围。如果存放在网站的根路径下,则将会收到该网站的所有 fetch 事件。如果希望改变它的作用域,可在第二个参数设置 scope 范围
navigator.serviceWorker
.register("serviceWorker.js")
.then(function (registration) {
console.log("ServiceWorker注册成功: ", registration.scope);
})
.catch(function (err) {
console.log("ServiceWorker注册失败: ", err);
});
}

注册完成后,serviceWorker.js 文件会自动下载,然后安装,最后激活。

service worker 中的常用事件

  1. install
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var cacheName = "hello-pwa";

// install 事件,它发生在浏览器安装并注册 Service Worker 时
self.addEventListener("install", (event) => {
// event.waitUtil 用于在安装成功之前执行一些预装逻辑,但是建议只做一些轻量级和非常重要资源的缓存,减少安装失败的概率,安装成功后 ServiceWorker 状态会从 installing 变为 installed
event.waitUntil(
caches
.open(cacheName)
.then((cache) =>
cache.addAll(
[
"/", // 这个一定要包含整个目录,不然无法离线浏览
"./images/cat2.jpg",
"./index.html",
"./style.css",
]
// 如果所有的文件都成功缓存了,便会安装完成。如果任何文件下载失败了,那么安装过程也会随之失败。
)
)
.then(() => self.skipWaiting())
);
});

service worker 会等到 waitUntil 里面的代码执行完毕之后才开始安装,它返回一个 promise。
caches 是一个特殊的 CacheStorage 对象,它能在 Service Worker 指定的范围内提供数据存储的能力。

  1. fetch

每次当我们的应用发起一个 http 请求时,我们还有一个 fetch 事件可以使用。这个事件对我们来说非常有用,它允许我们拦截请求并对请求作出自定义的响应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 为 fetch 事件添加一个事件监听器,service worker将从缓存中请求所需的数据,从而提供离线应用功能
self.addEventListener("fetch", function (e) {
event.respondWith(
// 使用 caches.match() 函数来检查传入的请求 URL 是否匹配当前缓存中存在的任何内容,如果存在的话,返回缓存的资源;如果资源并不存在于缓存当中,通过网络来获取资源,并将资源存储到缓存中。
caches.match(e.request).then(function (r) {
return (
r ||
fetch(e.request).then(function (response) {
return caches.open(cacheName).then(function (cache) {
cache.put(e.request, response.clone());
return response;
});
})
);
})
);
});

Manifest

为了成为可安装网站,需要下列事情就位:

  • 一份网页清单,填好正确的字段
  • 网站的域必须是安全(HTTPS)的
  • 一个本设备上代表应用的图标
  • 一个注册好的 service worker,可以让应用离线工作(这仅对于安卓设备上的 Chrome 浏览器是必需的)

清单文件

清单文件通常位于网页应用的根目录,包含一些有用的信息,比如应用的标题,在一个移动 OS 上显示的代表该应用的不同大小的图标(例如,主屏图标)的路径,和用于加载或启动画面的背景颜色。这些信息是浏览器在安装 web 应用时和在主屏上显示应用需要的,这些信息是以 JSON 的形式列出来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"name": "Minimal PWA", //网站应用的全名
"short_name": "PWA Demo", // 显示在主屏上的短名
"description": "The app that helps you understand PWA", //一两句话解释你的应用的用途
"display": "standalone", // 应用的显示方式:可以是全屏,独立,最小ui或者浏览器
"start_url": "/", //应用启动的index文档
"theme_color": "#313131", // ui的主题色,这是操作系统使用的
"background_color": "#313131", // 背景色,用于安装程序时和启动应用时
"icons": [
{
"src": "icon/lowres.webp",
"sizes": "48x48",
"type": "image/webp"
},
{
"src": "icon/lowres",
"sizes": "48x48"
},
{
"src": "icon/hd_hi.ico",
"sizes": "72x72 96x96 128x128 256x256"
},
{
"src": "icon/hd_hi.svg",
"sizes": "72x72"
}
] //一串图标信息——源URL,大小和类型,确保包含一些图标,这样有一个最适合用户设备的图标可以被选中
}

一份网页清单最少需要 name 和一个图标 (带有 src, size 和 type)。description, short_name, 和 start_url 最好要提供。

扩展以下,过去有一些常用的扩展名用于清单:manifest.webapp 在 Firefox OS 应用清单中很流行,许多人使用 manifest.json 作为网页清单因为内容是 JSON 格式的。但是,.webmanifest 扩展名是在 W3C 清单规范中显示指定的,建议使用清单文件使用.webmanifest 作为后缀 。

添加到主屏

“添加到主屏”是移动浏览器实现的一个特性,它利用网页清单中的信息来在设备主屏上显示应用图标和文字。当用户使用一个支持的移动浏览器访问一个 PWA 时,会显示一个弹框表示可以安装这个应用,用户确认之后应用就被安装到主屏了,用户可以立刻启动并使用应用。在一些浏览器中,可以通过清单信息产生一个启动画面,当 PWA 启动时显示,图标、主题和背景色用于创建这个启动画面。

在 ios 上和 android 手机上打开 vue 的官网,可以将其添加到设备主屏:

  • ios

iosios

  • android

androidandroid

Push&Notification

Push&Notification 即推送和通知,通过推送 API 和通知 API 来实现。

  • 推送可以实现从服务端推送新的内容而不需要客户端发起请求,它是由应用的 service worker 来实现的。通知功能则可以通过 service worker 来向用户展示一些新的信息,或者至少提醒用户应用已经更新了某些功能。
  • 这些工作是在浏览器外部实现的,跟 service worker 一样,所以即使应用被隐藏到后台甚至应用已经被关闭了,我们仍然能够推送更新或者推送通知给用户。
  • 推送 API 和通知 API 可以独立工作,也可以结合到一起使用。

通知 API——请求授权

当用户确定接收通知,我们的应用就可以获得推送通知的功能。用户的授权的结果有三种,default,granted 或者 denied,当用户没有做出选择的时候,授权结果会返回 defalut,另外两种结果分别是用户选择了授权或者拒绝授权。一旦用户选择授权,这个授权结果对通知 API 和推送 API 两者都有效。

1
2
3
4
5
6
7
8
9
10
var button = document.getElementById("notifications");
console.log(button);
button.addEventListener("click", function (e) {
Notification.requestPermission().then(function (result) {
if (result === "granted") {
// randomNotification();
console.log("授权");
}
});
});

推送

推送比通知要复杂一些,我们需要从服务端订阅一个服务,之后服务端会推送数据到客户端应用。应用的 Service Worker 将会接收到从服务端推送的数据,这些数据可以用来做通知推送,或者实现其他的需求,这个技术还处在非常初级的阶段。
为了接收到推送的消息,你需要有一个 service worker,在 service worker 内部,存在一个消息推送服务订阅机制。

1
registration.pushManager.getSubscription().then(/* ... */);

一旦用户订阅服务,他们就能接收到服务器推送的通知。为了能够接收到推送的消息,我们需要在 Service Worker 文件里面监听 push 事件。

1
2
3
self.addEventListener("push", function (e) {
/* ... */
});

这个技术还处在非常初级的阶段,从服务端的角度来看,出于安全的目的,这整个过程必须使用非对称加密技术进行加密。而VAPID可以为你的应用提供一层额外的安全保护。

发展趋势

既然谈到 PWA 的发展趋势,就不得不说说它的优势和劣势了。

优势

  • 无需安装,无需下载,只要你输入网址访问一次,然后将其添加到设备桌面就可以持续使用。
  • 发布不需要提交到 app 商店审核,更新迭代版本不需要审核
  • 现有的 web 网页都能通过改进成为 PWA, 能很快的转型,上线,实现业务、获取流量
  • 不需要开发 Android 和 IOS 两套不同的版本

劣势

  • 浏览器对技术支持还不够全面, 不是每一款游览器都能 100%的支持所有 PWA
  • 需要通过第三方库才能调用底层硬件(如摄像头)
  • PWA 现在还没那么火,国内一些手机生产上在 Android 系统上做了手脚,似乎屏蔽了 PWA

PWA 作为一个 2016 年才落地的新技术,经过四年的发展,基于 Chromium 的浏览器 Chrome 和 Opera 已经完全支持 PWA 了,随着 iOS 11.3 的发布,iOS 正式开始支持 PWA,Windows Edge 也支持 PWA 了。越来越多的游览器大厂,相继的对 PWA 做出了支持和优化,想必 PWA 的时代即将到来。

关于 PWA,你怎么看呢?

作者

曹芯萍

发布于

2020-08-06

更新于

2021-08-04

许可协议

CC BY-NC-SA 4.0

评论