1.关于PWA
PWA,全称Progressive web apps,即渐进式Web应用。PWA技术主要作用为构建跨平台的Web应用程序,并使其具有与原生应用程序相同的用户体验。
早在2014年,W3C就已经公布了PWA的相关技术草案,Chrome于2015年在生产环境下落地支持,但是由于兼容性的问题,PWA技术并没有引起广泛的关注。一直到2018年,苹果在IOS 11.3中正式支持PWA,PWA才又重新回到了广大技术开发人员的视野中。
2.为什么使用PWA
传统Web存在的问题:
1.缺乏直接性的入口。打开网站需要记住它的域名,即使将它保存在收藏夹,寻找起来也不够直接,不够简便。
2.依赖于网络。只要客户机处于断网状态,整个Web系统就处于瘫痪状态,在客户机上无法使用。
3.无法向Native APP一样推送消息。
传统Native APP存在的问题:
1.需要下载与安装。哪怕我只是使用这款APP的一个功能,或者临时使用一下这个APP,我也需要全盘下载并安装APP。
2.开发成本高。你需要准备安卓和IOS两个版本。
3.发布需要审核。在APP Store或者安卓应用市场进行发布,都需要提交审核。
4.更新成本高。即使很小的程序更新,也会需要重新发版,重新提交应用商店审核。
5.内容无法被索引。由于其封闭性,Native APP的内容无法被预先索引,无法进行基于内容的APP检索,只能根据介绍去推断APP的内容。
PWA的存在,就是为了解决以上问题给用户带来的麻烦。
它的优势包括:
1.桌面入口,打开便捷。
2.离线可用,不过度依赖网络。
3.安装简便。
4.一次开发,无需审核,所有平台可用。
5.能够进行消息的推送。
3.PWA技术包括了什么
PWA并不是指某一个具体的技术,要实现PWA应用,需要多种技术的支持。
其中的核心技术包括了:Web App Manifest、Service worker、Cache API、Indexed DB、Push API、Notification API等等。
4.Service Worker与Cache API
读完了上边的内容,相信很多人还是云里雾里的,只是知道了个关于PWA的大概,但是对于如何构建一个PWA应用,还是一无所知。我就是这样的,当初为了学习PWA,参阅了很多资料,很多都是在讲类似于上边的内容,具体的内容却没有说的很明白。这里,我们就如何构建一个PWA应用,进行一下说明。
本质上,PWA就是一个网站,其根本的内容,还是通过HTML + CSS + Javascript去构建的Web页面。为了让它实现添加桌面入口、离线使用、消息推送等PWA特有的功能,就需使用一些Web API为我们的网站添加一些额外的代码。而其中最根本、最基本的,就是Service Worker以及在其内部使用的Cache API。只要通过Service Worker与Cache API,实现了对网站页面的缓存、对页面请求的拦截、对页面缓存的操纵,我认为一个简易的PWA应用就算搭建完成了。
1.什么是Service Worker
Service Worker是一个注册在指定源和路径下的事件驱动型Web Worker。它充当了Web应用程序与浏览器之间的代理服务器,进行资源在文件级别下的缓存与操控,拦截页面请求,实现在不同的情况下对不同请求的响应策略。
2.Service Worker的特点
首先,Service Worker本质上就是一个Web Worker,因此它具有Web Worker的特点:
1.无法操作DOM;
2.脱离主线程;
3.独立上下文;
除此之外,Service Worker还具有以下特点:
4.只能在Https下使用;
5.运行在浏览器后台,不受页面刷新影响;
6.更强大的离线缓存能力(使用Cache API);
7.请求拦截能力;
8.完全异步,不能使用同步API;
9.持续运行,第一次访问页面后,Service Worker就会安装激活并持续运行,直到手动销毁;
3.在页面中注册一个Service Worker
if('serviceWorker' in navigator){
window.addEventListener('load', function(){
navigator.serviceWorker.register('/sw.js', {scope: '/' }).then(function(registration){
console.log('ServiceWorker registration successful with scope: ' + registration.scope);
}, function(err){
console.log('ServiceWorker registration failed: ' + err);
});
});
}
我们使用navigator.serviceWorker.register
函数来注册Service Worker。其中,第一个参数为要执行的worker逻辑文件路径,注意这个路径是基于origin的,而非当前文件。第二个参数为Service Worker所能控制的文件范围,如果不填,则默认控制与sw.js文件同级范围下的所有文件。
4.Service Worker的生命周期
完成注册后,Service Worker开始进行安装,安装成功之后,会在worker中触发install
事件;如果安装失败,则进入废弃状态。
安装成功之后,Service Worker开始进行激活。激活成功之后,会在worker中触发activate
事件,这个时候,Service Worker就能够控制页面了,我们可以通过Service Worker去进行文件的缓存、请求的监听等操作。如果Service Worker激活失败,则会进入废弃状态。
如果Service Worker逻辑文件更新(相关资源文件变动或者内部逻辑更新等),Service Worker会重新安装,如果这个时候,页面依然存在激活状态下的worker(旧的Service Worker),那么新的worker会进入waiting状态进行等待,直到我们主动去操作worker强制其更新,或者等待用户关闭所有页面,这个时候新的worker才会进入到激活状态。
5.install事件
//sw.js
const CACHE_NAME = 'myCache';
let urlsToCache = [
'/html/index.html',
'css/index.css'
];
self.addEventListener('install', function(event){
event.waitUntil(
caches.open(CACHE_NAME).then(function(cache){
console.log('Open cache');
return cache.addAll(urlsToCache);
}).then(function(){
self.skipWaiting();
})
);
});
在install事件中,我们使用caches.open
方法打开cache对象,并通过cache.addAll
缓存所有我们列出的文件。
如果Service Worker存在更新,我们使用skipWaiting
跳过等待,直接强制新的worker进入激活状态。
6.fetch事件
//sw.js
self.addEventListener('fetch', function(event){
if(event.request.method !== 'GET') return;
event.respondWith(
caches.match(event.request).then(function(response){
if(response){
console.log('return caches');
return response;
}else{
return fetch(event.request).catch(function(){
if(/\.html$/.test(event.request.url))
return caches.match('/html/neterror.html');
});
}
})
)
});
这里我们只监听了get请求,即我们只希望控制资源请求。通过caches.match
检查请求是否命中了缓存,如果命中,则直接返回缓存给用户,防止重复请求,节约资源。如果没有命中,则将使用fetch
方法请求网络资源并返回给用户。当网络状态异常时(fetch().catch()
),返回404页面的缓存给用户,告知用户当前处于无网络状态,不能访问相关页面。
这种写法适用的情况为:我们指定了一些页面进行缓存,我们希望用户在无网络的情况下只能访问到我们指定缓存的页面。
当然,还有一种情况为,我们指定了一些页面进行缓存(常用页面),当用户访问到一些不常用页面时,再对其进行缓存。这样,我们对资源配置进行了优化,不过多的占用用户本地资源去缓存所有页面,对于非所有用户的常用页面,按需缓存。
self.addEventListener('fetch', function(event){
if(event.request.method !== 'GET') return;
event.respondWith(
caches.match(event.request).then(function(response){
if(response){
console.log('return caches');
return response;
}else{
return fetch(event.request).then(function(res){
var responseToCache = res.clone();
caches.open(CACHE_NAME).then(function(cache){
catch.put(event.request, responseToCache);
})
return res;
});
}
})
)
});
7.至此,一个简易的PWA应用,就算是完成了。
5.结束语
作为一个PWA的初学者,我也仅仅是把现阶段学到的部分总结在这里,还有很多的问题和情况都没有想到、遇到。这些等以后再慢慢的补充。文章或者代码中肯定也有许多讲的不合适、不规范的地方,如果有大佬看到了,还望提点。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。