概述
不知道从什么时候开始,当我们在浏览网页时,页面会出现一个弹窗,询问我们是否要将此网页保存到本地,如果我们选择了确定,在设备的主屏上就会出现一个新的app,而它就是我们刚才浏览的网页。这种app我们并没有从应用商店去下载,但是我们可以像使用app一样去使用它,也可以像卸载其他app一样去将它卸载,那么这究竟是个什么东西呢,它就是这篇文章的主角——PWA。
PWA是Progressive Web App的简写,中文名称为渐进式web应用。它是Google 在2016年提出的概念,2017年落地的web技术,一种在移动端利用提供的标准化框架,在网页应用中实现和原生应用相近的用户体验的渐进式网页应用。PWA不单指一种技术,你也可以将其理解为一种思想和概念,目的就是对标原生app,将Web网站通过一系列的Web技术去优化它,提升其安全性,性能,流畅性,用户体验等各方面指标,最后达到用户就像在用app一样的感觉。
说到app,我们就不得不提起native app即原生app,现在非常主流的移动端应用。原生app使用起来会很流畅,性能好,安全性也可以很高,这是它很显著的优势。但是缺点呢,也很明显,比如:
1. 开发成本很昂贵
2. 软件上线,版本更新都需要发布到不同的商店,并通过审核
这对开发人员来说是比较麻烦的事情。
而对于用户来说,有些APP可能使用频率特别少,但还是不得不去商店中下载庞大的安装包,或者可能一段时间不使用以后,随着版本的更新,也不得不去重新更新并安装。
PWA则完美地避免了这些问题。
要判断一个web应用是否是PWA,可以从以下几个方面去考虑:
1. 可发现的—— 内容可以通过搜索引擎发现。
2. 可安装—— 可以出现在设备的主屏幕。
3. 可链接—— 你可以简单地通过一个URL来分享它。
4. 独立于网络——它可以在离线状态或者是在网速很差的情况下运行。
5. 渐进式—— 它在老版本的浏览器仍旧可以使用,在新版本的浏览器上可以使用全部功能。
6. 可重用—— 无论何时有新的内容它都可以发送通知。
7. 响应性—— 它在任何具有屏幕和浏览器的设备上可以正常使用——包括手机,平板电脑,笔记本,电视,冰箱,等。
8. 安全—— 在你和应用之间的连接是安全的,可以阻止第三方访问你的敏感数据。
当然,在安装方式上PWA应用与原生app有很大的不同,但是在实际使用上,与原生应用的差距非常小,对于用户来说,几乎是无感的。
Service Workers是浏览器和网络之间的虚拟代理,运行在一个与页面的 JavaScript 主线程独立的线程上,并且没有对 DOM 结构的任何访问权限,并且可以在不同的上下文之间发送和接收信息。 您可分配给 Service Worker 一些任务,并使用基于 Promise 的方法在任务完成时收到结果。他们不仅仅提供离线功能,还提供包括处理通知,在单独的线程上执行繁重的计算等。Service workers 非常强大,因为他们可以控制网络请求,修改网络请求,返回缓存的自定义响应,或合成响应。因为它们非常强大,所以 Service Workers 只能在安全的上下文中执行(即 HTTPS )。
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 文件会自动下载,然后安装,最后激活。
1. install
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指定的范围内提供数据存储的能力。
2. fetch
每次当我们的应用发起一个http请求时,我们还有一个fetch 事件可以使用。这个事件对我们来说非常有用,它允许我们拦截请求并对请求作出自定义的响应。
// 为 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;
});
});
})
);
});
为了成为可安装网站,需要下列事情就位:
清单文件通常位于网页应用的根目录,包含一些有用的信息,比如应用的标题,在一个移动OS上显示的代表该应用的不同大小的图标(例如,主屏图标)的路径,和用于加载或启动画面的背景颜色。这些信息是浏览器在安装web应用时和在主屏上显示应用需要的,这些信息是以JSON的形式列出来的。
{
"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的官网,可以将其添加到设备主屏:
Push&Notification即推送和通知,通过推送API和通知API来实现。
当用户确定接收通知,我们的应用就可以获得推送通知的功能。用户的授权的结果有三种,default,granted 或者denied,当用户没有做出选择的时候,授权结果会返回defalut,另外两种结果分别是用户选择了授权或者拒绝授权。一旦用户选择授权,这个授权结果对通知API和推送API两者都有效。
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 内部,存在一个消息推送服务订阅机制。
registration.pushManager.getSubscription() .then( /* ... */ );
一旦用户订阅服务,他们就能接收到服务器推送的通知。为了能够接收到推送的消息,我们需要在Service Worker文件里面监听push事件。
self.addEventListener('push', function(e) {
/* ... */ });
这个技术还处在非常初级的阶段,从服务端的角度来看,出于安全的目的,这整个过程必须使用非对称加密技术进行加密。而VAPID可以为你的应用提供一层额外的安全保护。
既然谈到PWA的发展趋势,就不得不说说它的优势和劣势了。
PWA作为一个2016年才落地的新技术,经过四年的发展,基于 Chromium 的浏览器 Chrome 和 Opera 已经完全支持 PWA 了,随着 iOS 11.3 的发布,iOS正式开始支持PWA,Windows Edge 也支持PWA了。越来越多的游览器大厂,相继的对PWA做出了支持和优化,想必PWA的时代即将到来。
关于PWA,你怎么看呢?