PWA初始
一、对PWA的一些自己的理解
PWA全称Progressive Web App,即渐进式WEB应用。一种可以提供类似于原生应用程序(native app)体验的网络应用程序(web app)。PWA 可以用来做很多事。其中最重要的是,在离线(offline)时应用程序能够继续运行功能。这是通过使用名为 Service Workers 的网络技术来实现的。
首先 PWA 仍然是一个网页。他不是一向新的框架或者新的技术,他是一系列Web技术的集合
PWA 使用到的主要技术有哪些呢? 他们是做什么的?
使用到的技术 作用 Web App Manifest 在主屏添加app图标 定义手机标题栏和隐藏导航栏 Service Worker 用来实现离线 Cache API 缓存技术 用来实现离线应用 Push Notification 消息推送
二、使用Manifest在主屏添加app图标
- chrome浏览器拥有将网页添加到手机主屏的功能。当点击这个选项后,会在主屏上增加一个网页的快捷方式。如下图
- 如果想让这个快捷方式更像原生应用,需要解决几个问题(这些问题manifest可以帮你解决)。
2.1 要有自己的应用图标。
2.2 打开时显示一个开屏图(不要有白屏)。
2.3 不要出现地址栏
- 添加manifest的步骤。
3.1 新建一个web项目文件夹,新增配置一个manifest.json的文件,参数说明。 Web App Manifest(MDN)
{
"name":"一个PWA示例",
"short_name":"PWA示例", // 应用显示在主屏的名字
"start_url":"/index.html",
"display":"standalone", // 设置启动样式,让你的网络应用隐藏浏览器的URL地址栏
"background_color":"#fff",
"theme_color":"#3eaf7c",
"icons":[
{
"src":"/icon.png",
"sizes":"120x120",
"types":"image/png"
}
]
}
3.2 然后在新建index页面,在页面中新增一条link。就OK拉 。去看看现在在主屏创建的效果。
<link rel="manifest" href="/manifest.json">
效果如下图
三、Service Worker增加离线的能力
解释在这里
什么是Service Worker?说一说我自己的理解,Service Worker我认为就好像一个运行在你本机中的软件,这个软件起到一个代理的作用,可以截获页面到服务器的请求(缓存离线),并可以接受服务器的消息(推送订阅),他的表现形式是运行一个js文件。特殊的点是:
- 它的下载是通过首次访问有Service Worker支持的网站时下载的。每24小时充实下载一次,当然也可能更频繁。在首次下载后 页面会直接安装它。安装完以后它会被激活。
- 它是在浏览器主进程之外单独跑的一个后台进程,这个进程跑的方法是在我们自己写的Service Worker中定义。
- 即使你离开了浏览器,这个Service Worker 也不会消失 仍然会在应用背后运行。
他的生命周期为
install -> installed -> actvating -> Active -> Activated -> Redundant 进入 Redundat状态的条件是:
- 安装(install)失败
- 激活(activating)失败
- 新版本的 Service Worker 替换了它并成为激活状态
(另外注意的是 Service Worker只能运行在https的域名下 或则是 localhost的本机调试中。我们可以用gitbutpage来做,很方便 后面会有演示)
使用Service Worker实现离线
(代码都放到了gitpage上了。地址点击这里)。
实现代码说明:
- 在html页面中注册Service Worker。注册前别忘了检验一下浏览器是否支持。注册后你的页面就拥有了Service Worker能力了 只不过现在它还啥都没做。
<script>
// 检测浏览器是否支持SW
if(navigator.serviceWorker != null){
// 其中sxf_sw.js就是我们实现Service worker方法的js文件
navigator.serviceWorker.register('sxf_sw.js').then(function (registortion){
console.log('支持sw', registortion.scope);
}).catch(function (whut) {
console.log('service worker has registered failure!');
})
}
</script>
- 编写sxf_sw.js,文件 让他拥有相应的能力。
//cachestorage名称,可以加上版本号予以区分
const OFFLINE_CACHE_PREFIX = 'sxf_offline_page_';
const CACHE_VERSION = 'v1.0';
const OFFLINE_CACHE_NAME = OFFLINE_CACHE_PREFIX + CACHE_VERSION;
//Service Worker安装事件,其中可以预缓存资源
this.addEventListener('install', function(event) {
console.log('install');
//需要缓存的页面资源这里缓存了三个资源。在安装过程中最好不要缓存太多,可能导致安装失败
var urlsToPrefetch = [
'index.html',
'main.css',
'icon.png'
];
event.waitUntil(
caches.open(OFFLINE_CACHE_NAME).then(function(cache) {
return cache.addAll(urlsToPrefetch);
})
);
});
//Service Worker激活事件
this.addEventListener('activate', function(event) {
//在激活事件中清除非当前版本的缓存避免用户存储空间急剧膨胀
event.waitUntil(caches.keys().then(function(cacheNames) {
return Promise.all(cacheNames.map(function(cacheName) {
if (cacheName !== OFFLINE_CACHE_NAME) {
if(cacheName.indexOf(OFFLINE_CACHE_PREFIX) != -1) {
return caches.delete(cacheName);
}
}
}));
}));
});
//Service Worker 请求拦截事件
this.addEventListener('fetch', function(event) {
event.respondWith(
caches.open(OFFLINE_CACHE_NAME).then(function(cache) {
return cache.match(event.request.url);
}).then(function(response){
//response为空表明未匹配成功,交由fetch方法去网络拉取
if(response) {
return response;
}
return fetch(event.request);
})
);
});
- 当功能写完以后如何测试我们的功能是否成功呢?这里使用gitpage
先注册一个git的仓库。将代码上传到这个仓库里面。然后点击Settings选项卡。配置gitpage页面如下图
进入Settings 标签后将,找到下面的Github Pages中的source选项。选择master branch。如下图 点击确定后就可以在上面看到你的GitHubPages地址了。然后拷贝这个地址。在后面加上index.html。 进入页面。可以看到Hello PWA。然后点击F12进入开发者控制台。选择Application选项。并选择左侧的Service Workers可以看到如下图。
- 更新缓存时 只需要修改CACHE_VERSION 这个变量就可以了。
使用Service Worker实现推送订阅功能
(这一部分有个讲的非常好的blog我也是受益匪浅 地址点击这里) 下面桌一些自己的理解: (1) 推送订阅的实现原理并非是我们原来的长连接 或者WebsocketD的形式。它使用了一种叫Web Push的技术。在web push中有三方。
- 浏览器:也就是我们的客户端
- Push Service: 这个由浏览器厂商提供,Chrome 和 firefox都有自己的。
- 后端服务:我们自己的服务。下图是引用 协议草案的流程图
+-------+ +--------------+ +-------------+
| UA | | Push Service | | Application |
+-------+ +--------------+ | Server |
| | +-------------+
| Subscribe | |
|--------------------->| |
| Monitor | |
|<====================>| |
| | |
| Distribute Push Resource |
|-------------------------------------------->|
| | |
: : :
| | Push Message |
| Push Message |<---------------------|
|<---------------------| |
| | |
步骤说明:
- 现在浏览器端订阅消息服务。(这个订阅是和PUSH Service 订阅)。订阅成功后会得到一个PushSubscription 对象。
- 将这个对象的信息发送给我们的后端服务。
- 我们后端服务通过使用 web-push 这个组件的方法可以将消息推送给Push Service。 推送是需要用到 PushSubscription 对象中的endpoint
- Push Service 根据传递过来的endpoint 找到对应的浏览器,将消息推送给浏览器上的Service Worker 。
- 浏览器的 Service Worker 通过监听 push方法可以将数据得到并进行处理。
在数据的传输过程中需要用到一对公钥私钥进行签名加密,以保证浏览器接收到的信息是自己服务器发送出来的。
(2) 一个没有后台服务的简单实现前端订阅的代码和测试方法。
首先改造我们上面的 index.html 页面的 js 方法如下代码
// 检测浏览器是否支持SW
/**
* base64的相关操作,具体可以参考
* https://github.com/web-push-libs/web-push#using-vapid-key-for-applicationserverkey
*/
window.urlBase64ToUint8Array = function (base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
// if(navigator.serviceWorker != null){
// navigator.serviceWorker.register('sxf_sw.js').then(function (registortion) {
// console.log('支持sw', registortion.scope);
// }).catch(function (whut) {
// console.log('service worker has registered failure!');
// })
// }
function registerServiceWorker(file) {
return navigator.serviceWorker.register(file);
}
function subscribeUserToPush(registration, publicKey) {
var subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: window.urlBase64ToUint8Array(publicKey)
};
return registration.pushManager.subscribe(subscribeOptions).then(function (pushSubscription) {
console.log('Received PushSubscription: ', JSON.stringify(pushSubscription));
return pushSubscription;
});
}
if ('serviceWorker' in navigator && 'PushManager' in window) {
var publicKey = 'BOEQSjdhorIf8M0XFNlwohK3sTzO9iJwvbYU-fuXRF0tvRpPPMGO6d_gJC_pUQwBT7wD8rKutpNTFHOHN3VqJ0A';
// 注册service worker
registerServiceWorker('sxf_sw.js').then(function (registration) {
console.log('Service Worker 注册成功');
// 开启该客户端的消息推送订阅功能
return subscribeUserToPush(registration, publicKey);
}).then(function (subscription) {
var body = {subscription: subscription};
// 为了方便之后的推送,为每个客户端简单生成一个标识
body.uniqueid = new Date().getTime();
console.log('uniqueid', body.uniqueid);
// 将生成的客户端订阅信息存储在自己的服务器上
return sendSubscriptionToServer(JSON.stringify(body));
}).then(function (res) {
console.log(res);
}).catch(function (err) {
console.log(err);
});
}
function sendSubscriptionToServer (endpointobj) {
console.log("存储成功"+endpointobj.toString())
}
然后在 sxf_sw.js 中增加方法
// 监听推送的消息
self.addEventListener('push', function (e) {
var data = e.data;
if (e.data) {
data = data.json();
console.log('push的数据为:', data);
self.registration.showNotification(data.text);
}
else {
console.log('push没有任何数据');
}
});
测试时 打开页面,浏览器会询问是否允许通知,点击允许。然后和测试一样进入下图画面
此上就是对PWA的浅薄学习,如果有说的不对的 请大家指出