上節課咱們一塊兒瞭解了sevice worker,它能夠用於客戶端資源緩存。但sevice worker的用處遠遠不止於此,它能夠攔截全部的客戶端http請求並存儲返回的response,從而實現離線的客戶端正常訪問。 離線訪問這一特性很容易讓人聯想到Native App的能力,實際上,sevice worker配合其餘的技術,確實是可讓web頁'Native'化,這就是咱們常說的 PWA。javascript
PWA(progress web app)是致力於在網頁應用中實現和原生應用相近的用戶體驗的漸進式網頁應用,由google於2016年提出。 雖然是web頁面,但經過一些新技術,使它具備離線訪問和推送的能力,同時與native app相比,又具備安裝方便、更新便捷的特色。html
能夠訪問百度的LAVAS首頁,獲取更多關於pwa的API信息 lavas.baidu.com/pwa/README前端
一個標準的PWA應該具備如下特色java
從技術層面來講,pwa應實現如下功能:web
sevice worker是由js編寫,運行在應用程序和瀏覽器之間的代理服務器,給 web 應用提供高級的可持續的後臺處理能力。 它可以建立有效的離線體驗,攔截網絡請求並基於網絡是否可用以及更新的資源是否駐留在服務器上來採起適當的動做。chrome
具體的sevice worker教程,能夠參考咱們以前的文章 天天一點網站優化之:前端靜態資源緩存 sevice worker數據庫
經過在sevice worker中設置資源的離線緩存規則,能夠爲web應用提供離線訪問的能力。npm
注意:pwa要求web應用必需要有離線訪問能力。json
容許將站點添加至主屏幕,是 PWA 提供的一項重要功能。雖然目前部分瀏覽器已經支持向主屏幕添加網頁快捷方式以方便用戶快速打開站點,可是 PWA 添加到主屏幕的不只僅是一個網頁快捷方式, 它將提供更多的功能,更多的樣式,讓 PWA 具備更加原生的體驗。 PWA 添加至桌面的功能實現依賴於 manifest.json,能夠經過manifest.json文件配置應用的圖標,名稱等信息。api
// manifest.json文件配置
{
"short_name": "短名稱",
"name": "這是一個完整名稱", //定義名稱
"icons": [ //自定義圖標
{
"src": "love0.png",
"type": "image/png",
"sizes": "48x48"
},
{
"src": "love.png",
"type": "image/png",
"sizes": "144x144"
}
],
"start_url": "index.html", //指定應用打開的url
"display": "fullscreen", //應用打開的展現窗口樣式
"theme_color": "#ff4c43" //配置應用打開後窗口的顏色
}
複製代碼
<link rel="manifest" href="manifest.json">
複製代碼
打開web頁,若是這個web頁面支持pwa,則能夠經過瀏覽器右上角的更多安裝應用
manifest.json文件寫定的規則一旦安裝,就會一直生效,若是想要修改manifest信息並生效,須要卸載舊的應用程序並從新安裝。 能夠在pwa中點擊右上角三個點選擇卸載,或者是在文件夾中直接刪除應用。
pwa的推送功能經過Push API 和 Notifications API 實現,其中 PUSH API負責消息推送, Notification API負責展現提醒。
web push 的原理
push Service能夠在用戶離線時保存消息隊列,在上線後統一發送。而且,Push Service會爲每一個發起訂閱的瀏覽器生成一個惟一的URL, 這樣,咱們在服務端推送消息時,向這個URL進行推送後,Push Service就會知道要通知哪一個瀏覽器,保存url信息的字段是endpoint.
// 註冊sevice worker
if ('serviceWorker' in navigator) {
var publicKey = 'BOEQSjdhorIf8M0XFNlwohK3sTzO9iJwvbYU-fuXRF0tvRpPPMGO6d_gJC_pUQwBT7wD8rKutpNTFHOHN3VqJ0A';
registerServiceWorker()
.then(registration => {
console.log('ServiceWorker 註冊成功!做用域爲: ', registration.scope)
// 發起訂閱 push sevice 給客戶端返回惟一標識
return subscribeUserToPush(registration, publicKey);
})
.then(subscription => {
var body = {subscription: subscription};
// 將生成的客戶端訂閱信息存儲在本身的服務器上
return sendSubscriptionToServer(JSON.stringify(body));
})
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err)
});
}
// 註冊
function registerServiceWorker() {
return navigator.serviceWorker.register('sw.js', {scope: '/'});
}
// 發起訂閱
function subscribeUserToPush(registration, publicKey) {
var subscribeOptions = {
userVisibleOnly: true, //推送是否須要顯性發送給用戶
applicationServerKey: publicKey
};
return registration.pushManager.subscribe(subscribeOptions).then(function (pushSubscription) {
console.log('Received PushSubscription: ', JSON.stringify(pushSubscription));
return pushSubscription;
});
}
複製代碼
服務端代碼:
const webpush = require('web-push');
// pwa推送信息
const pushMessage = async (ctx) => {
/** * VAPID值 * 這裏能夠替換爲你業務中實際的值 */
const vapidKeys = {
publicKey: 'BOEQSjdhorIf8M0XFNlwohK3sTzO9iJwvbYU-fuXRF0tvRpPPMGO6d_gJC_pUQwBT7wD8rKutpNTFHOHN3VqJ0A',
privateKey: 'TVe_nJlciDOn130gFyFYP8UiGxxWd3QdH6C5axXpSgM'
};
// 設置web-push的VAPID值
webpush.setVapidDetails(
'mailto:xxx@qq.com',
vapidKeys.publicKey,
vapidKeys.privateKey
);
// 須要推送的客戶端信息,此處能夠根據業務場景從數據庫中獲取
let subscription = {
endpoint:'https://updates.push.services.mozilla.com/wpush/v2/gAAAAABdJ-rl96NWkfof3N3cHVJ0vO2-i_9K5eg2WoS9XumIJyYSp-Eeu2MpEV0qoisZipI2mbsYRvceM7F_62QJ0hjsAES8qGflnMmkB_UZjzIi8dI5SGIGrCh2RPurGdrdVL4o9yVo6dx8RfI6MHIqNyaqxYTOC_jH61EtSP9inn_eYdMRw3c',
keys:{
auth:'ehJWs1HwbokEKzf7VDhORQ',
p256dh:'BBNLr0Qjib3QOHN2sFnjWR9Xtcm0kGSzDbqyh7FoXBalUD_yqRBCgBa8oXYIRL_vhdTW5x0hNI_vc_noT_1ekPc'
}
},
data = {
title : '我是通知標題',
body : 'We have received a push message.',
icon : 'love.png',
tag : 'simple-push-demo-notification-tag'
}
webpush.sendNotification(subscription, data).then(data => {
console.log('push service的相應數據:', JSON.stringify(data));
ctx.body = {
success:true,
message:'推送消息成功!'
}
return;
}).catch(err => {
// 判斷狀態碼,440和410表示失效
if (err.statusCode === 410 || err.statusCode === 404) {
console.log('該subscription已失效');
}
else {
console.log(err);
}
})
}
複製代碼
notification負責把消息從sw傳遞給客戶端
// sw.js文件
self.addEventListener('push', function(event) {
var
var body = 'We have received a push message.'; //吐送內容
var icon = '/love.png'; //顯示在推送上的圖標
var tag = 'simple-push-demo-notification-tag'; //string類型,tag相同的推送將自動覆蓋,防止推送消息太多致使的用戶體驗差
var data = {
doge: {
wow: 'such amaze notification data'
}
};
event.waitUntil(
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag,
data: data
})
);
});
self.addEventListener('notificationclick', event => {
console.log('用戶點擊了推送消息')
});
複製代碼