天天一點網站優化之:從 web 到 pwa


title: 天天一點網站優化之:從 web 到 pwa date: 2019-07-12 17:00:00 tags: -JavaScript categories: JavaScript

上節課咱們一塊兒瞭解了sevice worker,它能夠用於客戶端資源緩存。但sevice worker的用處遠遠不止於此,它能夠攔截全部的客戶端http請求並存儲返回的response,從而實現離線的客戶端正常訪問。 離線訪問這一特性很容易讓人聯想到Native App的能力,實際上,sevice worker配合其餘的技術,確實是可讓web頁'Native'化,這就是咱們常說的 PWA。javascript

PWA介紹

PWA(progress web app)是致力於在網頁應用中實現和原生應用相近的用戶體驗的漸進式網頁應用,由google於2016年提出。 雖然是web頁面,但經過一些新技術,使它具備離線訪問和推送的能力,同時與native app相比,又具備安裝方便、更新便捷的特色。html

能夠訪問百度的LAVAS首頁,獲取更多關於pwa的API信息 lavas.baidu.com/pwa/README前端

一個標準的PWA應該具備如下特色java

  • Discoverable, 內容能夠經過搜索引擎發現。
  • Installable, 能夠出如今設備的主屏幕。
  • Linkable, 你能夠簡單地經過一個URL來分享它。
  • Network independent, 它能夠在離線狀態或者是在網速不好的狀況下運行。
  • Progressive, 它在老版本的瀏覽器仍舊可使用,在新版本的瀏覽器上可使用所有功能。
  • Re-engageable, 不管什麼時候有新的內容它均可以發送通知。
  • Responsive, 它在任何具備屏幕和瀏覽器的設備上能夠正常使用——包括手機,平板電腦,筆記本,電視,冰箱,等。
  • Safe, 在你和應用之間的鏈接是安全的,能夠阻止第三方訪問你的敏感數據。

瀏覽器兼容性

image

PWA的實現

從技術層面來講,pwa應實現如下功能:web

  • 離線可訪問
  • 能夠從桌面圖標進入
  • 能夠實現用戶推送

離線緩存 sevice worker

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

實現

  1. 向項目目錄中添加 manifest.json
// 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" //配置應用打開後窗口的顏色
}
複製代碼
  1. 在項目首頁引入 manifest.json 文件
<link rel="manifest" href="manifest.json">
複製代碼
  1. 打開網站並安裝 pwa在pc端須要用戶手動安裝 此處以 mac os系統chrome瀏覽器舉例 首先須要開啓chrome瀏覽器的容許安裝pwa功能 在地址欄輸入 chrome://flags ,將Desktop PWAs 設置爲Enabled並重啓瀏覽器

打開web頁,若是這個web頁面支持pwa,則能夠經過瀏覽器右上角的更多安裝應用

image
瀏覽器彈出是否安裝應用確認框,點擊確認
image
安裝成功後,就能夠在應用程序中看到咱們的pwa入口
image
點擊pwa入口進入,沒有tab標籤和地址欄,但能夠實現打開控制檯,調試js等功能
image

manifest.json文件寫定的規則一旦安裝,就會一直生效,若是想要修改manifest信息並生效,須要卸載舊的應用程序並從新安裝。 能夠在pwa中點擊右上角三個點選擇卸載,或者是在文件夾中直接刪除應用。

消息推送與提醒

pwa的推送功能經過Push API 和 Notifications API 實現,其中 PUSH API負責消息推送, Notification API負責展現提醒。

消息推送

web push 的原理

image
上面的原理圖中有一個重要角色:push Service,你能夠認爲它是一個第三方的推送服務器,web 推送的消息必須經過 push Service轉發。google和FireFox都有本身的 pushService,但他們遵循同一套push協議規則。

push Service能夠在用戶離線時保存消息隊列,在上線後統一發送。而且,Push Service會爲每一個發起訂閱的瀏覽器生成一個惟一的URL, 這樣,咱們在服務端推送消息時,向這個URL進行推送後,Push Service就會知道要通知哪一個瀏覽器,保存url信息的字段是endpoint.

  1. Subscribe:瀏覽器(客戶端)須要向Push Service發起訂閱(subscribe),訂閱後會獲得一個PushSubscription對象
  2. Monitor:訂閱操做會和Push Service進行通訊,生成相應的訂閱信息,Push Service會維護相應信息,並基於此保持與客戶端的聯繫
  3. Distribute Push Resource:瀏覽器訂閱完成後,會獲取訂閱的相關信息(存在於PushSubscription對象中), 咱們須要將這些信息發送到本身的服務端,在服務端進行保存。
  4. Push Message:服務端把消息發送給push Service。
  5. Push Message:push Service接收到推送消息,跟維護的訂閱列表比對,把消息發送給指定的客戶端
  • google的push Service在國內不可用,因此如下咱們用firefox舉例

發起訂閱和保存訂閱信息

// 註冊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;
    });
}
複製代碼
  • publicKey是爲了保證安全推送,保存在客戶端。
  • 客戶端經過registration.pushManager.subscribepush方法調用 push 服務器,同時在參數中帶上了推送的設置和公鑰, 隨後push服務器將訂閱信息公鑰加密處理以後返回到客戶端
  • 客戶端將加密過的訂閱信息 subscription 發送到本身的服務器存儲, 服務器必須使用 subscription 才能給客戶端推送。

發送推送

服務端代碼:

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);
        }
    })
}
複製代碼
  • 服務端存儲 privateKey , 與客戶端發送訂閱時的 publicKey一一對應,這樣就能夠保證其餘服務器即便獲得了subscription, 也不能隨意向客戶端推送消息。
  • 發送推送依賴web push庫,能夠經過 npm install web-push 的方式安裝
  • 調用 webpush.sendNotification 方法並傳入惟一標識信息 subscription 就能夠向指定的客戶端發送推送消息

接收推送消息並顯示

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('用戶點擊了推送消息')
});
複製代碼
  • registration.showNotification 負責展現推送消息
  • 不一樣的系統會有不一樣的推送樣式
  • 用戶點擊推送消息能夠被 notificationclick 監聽到,能夠根據業務場景爲點擊事件增長不一樣的行爲 用postMan調用pushMessage接口,結果以下:
    image
相關文章
相關標籤/搜索