當前web應用在移動時代並無達到其在桌面設備上流行的程度,下面有張圖來對比與原生應用之間的差異。
究其緣由,無外乎下面不可避免的幾點:css
假如能解決以上的幾點,對web app 來講會有多大的提高能夠想象。html
PWA 全稱Progressive Web Apps(漸進式Web應用程序),旨在使用現有的web技術提供用戶更優的使用體驗。
基本要求java
PWA 自己強調漸進式,並不要求一次性達到安全、性能和體驗上的全部要求,開發者能夠經過 PWA Checklist 查看現有的特徵。 git
除以上的基準要求外,還應該包括如下特性:github
看起來有點眼花繚亂,這又是一個新的飛起的輪子嗎?這裏重申一下,PWA背後不是一種新的技術,而是集合當前多種web技術的一種集合。分別利用各自的功能來完成漸進式的總體需求。下面就沿着前面提出的問題分別瞭解一下相關技術web
由如下幾種技術構成:chrome
其中Service Worker是PWA技術的關鍵,它們可讓app知足上面的三基準。其餘技術則是錦上添花,讓app更加的強大。json
針對網頁的體驗,從前到後都作了不少努力,極力去下降響應時間,這裏就不表述多樣的技術手段。
另外一個方向的就是緩存,減小與服務器非必要的交互,不過對於離線的狀況下瀏覽器緩存就無力了,
這樣離線緩存的需求就出現了。數組
web應用在離線緩存發展的過程當中也不是一簇而就的,經歷了逐漸完善的過程。
初期的解決方案是AppCache
然而,事實證實這是一個失敗的嘗試,缺陷太多,已經被廢棄了。具體能夠查看Application Cache is a douchebag
可是方向仍是正確的,那就繼續孜孜不倦的探索。promise
持久化先放一邊,來談談另外一個問題
基於瀏覽器中的 javaScript 單線程的現實逐漸不能知足現代web需求的現狀,例如耗時的計算,用戶的交互顯然會受影響。
爲了將這些耗時操做從主線程中解放出來,早期W3C新增了一個Web Worker 的 API,能夠脫離主線程單獨執行,而且能夠與主線程交互。
不過Web Worker是臨時性的依賴於建立頁面 ,不能知足咱們持久化的需求。
衝着這個目標,下面就比較容易解決了,搞個能持久存在的就好了。
在Web Worker的基礎上,W3C新增了service worker來知足咱們持久化的需求。
其生命週期與頁面無關,關聯頁面未關閉時,它也能夠退出,沒有關聯頁面時,它也能夠啓動
功能
Service Worker雖然知足了離線緩存來,其功能可不只僅侷限於此。 能夠提供
這些正好也是PWA的目的,因此說Service Worker是PWA的關鍵技術。
Service Worker 出於安全性和其實現原理,在使用的時候有必定的前提條件。
由上可知,不是全部的瀏覽器都支持的,支持狀況大概以下:
iOS 內的全部的瀏覽器都基於 safari,因此iOS要在11.3以上
IE是放棄支持了,不過Edge好歹支持了。
Cache是Service Worker衍生出來的API,配合Service Worker實現對資源請求的緩存。
不過cache並不直接緩存字符串,而是直接緩存資源請求(css、js、html等)。
cache也是key-value形式,通常來講key就是request,value就是response
註冊即聲明sw文件的位置,顯然應該在主js中引入。大概以下:
//基於promise function registerServiceWorker(){ // 註冊service worker return navigator.serviceWorker.register('./sw1.js').then(registration => { console.log('註冊成功'); // 返回 return registration; }) .catch(err => { console.error('註冊失敗', err); }); } window.onload = function () { //是否支持 if (!('serviceWorker' in navigator)) { return; } registerServiceWorker() }
Service worker 有一個獨立於web 頁面的生命週期。
若是在網站上安裝 serice worker ,你須要註冊,註冊後瀏覽器會在後檯安裝 service worker。而後進入下面的不一樣階段。
激活以後,service worker 將控制全部的頁面,歸入它的範圍,不過第一次在頁面註冊 service worker 時不會控制頁面,直到它再次加載。
service worker 生效以後,它會處於下面兩種狀態之一:
由上圖看知,分爲這麼幾個階段:
瞭解聲明週期實際上是爲了咱們在不一樣時間段去監聽事件來完成相應操做。對PWA來講主要兩個事件。
event.waitUntil():傳入一個 Promise 爲參數,等到該 Promise 爲 resolve 狀態爲止。
self.skipWaiting():self 是當前 context 的 global 變量,執行該方法表示強制當前處在 waiting 狀態的 Service Worker 進入 activate 狀態。
event.waitUntil():傳入一個 Promise 爲參數,等到該 Promise 爲 resolve 狀態爲止。
self.clients.claim():在 activate 事件回調中執行該方法表示取得頁面的控制權, 這樣以後打開頁面都會使用版本更新的緩存。舊的 Service Worker 腳本再也不控制着頁面,以後會被中止。
const CURCACHE = 'CURCACHE_test_1' const RUNTIME = 'runtime'; const CURCACHE_URLS = [ './', '/asset/sw.jpg', 'index.js' ] self.addEventListener('install',e=>{ e.waitUntil( //存儲緩存路徑對應的資源 caches.open(CURCACHE).then(cache=>{ cache.addAll(CURCACHE_URLS) }).then( self.skipWaiting() ) ) }) //代理請求,使用緩存,請求發送以前 self.addEventListener('fetch', e => { e.respondWith( //緩存是否匹配 caches.match(e.request).then(function(response) { if (response != null) { //命中緩存返回緩存,結束請求 return response } //未命中緩存,正常請求 return fetch(e.request.url) }) ) });
更新service worker
service worker 更新步驟以下:
const CURCACHE = 'precache_test_1' //假設上個版本的key爲precache_test_2 反正不等於CURCACHE self.addEventListener('activate', e => { e.waitUntil( //遍歷當前緩存keys caches.keys().then(cacheNames=>{ return Promise.all( cacheNames.map(function(cacheName) { //是否等於當前key,保留本身 if (cacheName !== CURCACHE) { return caches.delete(cacheName); } }) )}).then(() => self.clients.claim()) ) })
這樣一個簡單的service worker離線緩存完成了。控制檯能夠看到,來源是service worker
關閉網絡以後再次訪問,能夠一樣獲得上面的結果,而且sw.js請求未能拿到,可是不影響,舊的文件依然在,這裏證實了每次都回去對比sw文件以確保更新
到這裏,離線緩存就實現了。
容許將站點添加至主屏幕,是 PWA 提供的一項重要功能。這樣就不用再依賴於瀏覽器做爲平臺,符合移動端的用戶習慣。
須要 manifest.json 文件去配置應用的圖標、名稱等基本信息以下:
{ //被提示安裝應用時出現的文本 "name": "PQJ-PWA", //添加至主屏幕後的文本 "short_name":"PQJ", "description": "測試demo", //添加以後,啓動地址 "start_url": "/index.html", //圖標信息 "icons": { "128": "/asset/sw.jpg" }, "developer": { "name": "pqj", "url": "" }, "display": "standalone", "background_color": "#287fc5", "theme_color": "#fff", "permissions": { "desktop-notification": { "description": "Needed for creating system notifications." } } }
而後以以下方式在html中引入
<link rel="manifest" href="/mainfest.json" />
這樣完成以後,移動端安卓使用chrome(親測),首次訪問時會提示是否容許安裝到主屏幕,以應用icon的形式出現。
圖片和文字即由配置決定。
消息通知也是使用service worker的通知功能進行的,容許服務器想用戶發生通知,而非用戶主動請求才去響應某些行爲。
正常的通知邏輯須要服務器來參與實現,此次展現只實現功能。
function getPermission(){ return new Promise((resolve, reject) => { //權限獲取 const permissionPromise = Notification.requestPermission(result => { resolve(result); }); }).then(result => { //判斷條件 if (result === 'granted') { execute(); } else { console.log('no permission'); } }); }
發送通知
function execute() { // 容許以後執行 registerServiceWorker().then(registration => { // 通知 registration.showNotification('Hello World!'); }); }
https://lavas.baidu.com/doc
https://developer.mozilla.org/zh-CN/Apps/Progressive
至此,本文介紹就結束了,更多請參考實例雖然PWA目前來看,面對的限制還不少,可是也能夠看出web組織在更好的提高web應用方向上作的努力。正如一直提到的那句話,將來可期。目前國內百度這方面作的比較成熟,新浪微博已經有了pwa 測試版。