PWA,全稱Progressive web apps,即漸進式Web應用。PWA技術主要做用爲構建跨平臺的Web應用程序,並使其具備與原生應用程序相同的用戶體驗。
早在2014年,W3C就已經公佈了PWA的相關技術草案,Chrome於2015年在生產環境下落地支持,可是因爲兼容性的問題,PWA技術並無引發普遍的關注。一直到2018年,蘋果在IOS 11.3中正式支持PWA,PWA才又從新回到了廣大技術開發人員的視野中。css
傳統Web存在的問題:html
1.缺少直接性的入口。打開網站須要記住它的域名,即便將它保存在收藏夾,尋找起來也不夠直接,不夠簡便。 2.依賴於網絡。只要客戶機處於斷網狀態,整個Web系統就處於癱瘓狀態,在客戶機上沒法使用。 3.沒法向Native APP同樣推送消息。
傳統Native APP存在的問題:web
1.須要下載與安裝。哪怕我只是使用這款APP的一個功能,或者臨時使用一下這個APP,我也須要全盤下載並安裝APP。 2.開發成本高。你須要準備安卓和IOS兩個版本。 3.發佈須要審覈。在APP Store或者安卓應用市場進行發佈,都須要提交審覈。 4.更新成本高。即便很小的程序更新,也會須要從新發版,從新提交應用商店審覈。 5.內容沒法被索引。因爲其封閉性,Native APP的內容沒法被預先索引,沒法進行基於內容的APP檢索,只能根據介紹去推斷APP的內容。
PWA的存在,就是爲了解決以上問題給用戶帶來的麻煩。
它的優點包括:瀏覽器
1.桌面入口,打開便捷。 2.離線可用,不過分依賴網絡。 3.安裝簡便。 4.一次開發,無需審覈,全部平臺可用。 5.可以進行消息的推送。
PWA並非指某一個具體的技術,要實現PWA應用,須要多種技術的支持。
其中的核心技術包括了:Web App Manifest、Service worker、Cache API、Indexed DB、Push API、Notification API等等。緩存
讀完了上邊的內容,相信不少人仍是雲裏霧裏的,只是知道了個關於PWA的大概,可是對於如何構建一個PWA應用,仍是一無所知。我就是這樣的,當初爲了學習PWA,參閱了不少資料,不少都是在講相似於上邊的內容,具體的內容卻沒有說的很明白。這裏,咱們就如何構建一個PWA應用,進行一下說明。
本質上,PWA就是一個網站,其根本的內容,仍是經過HTML + CSS + Javascript去構建的Web頁面。爲了讓它實現添加桌面入口、離線使用、消息推送等PWA特有的功能,就需使用一些Web API爲咱們的網站添加一些額外的代碼。而其中最根本、最基本的,就是Service Worker以及在其內部使用的Cache API。只要經過Service Worker與Cache API,實現了對網站頁面的緩存、對頁面請求的攔截、對頁面緩存的操縱,我認爲一個簡易的PWA應用就算搭建完成了。服務器
Service Worker是一個註冊在指定源和路徑下的事件驅動型Web Worker。它充當了Web應用程序與瀏覽器之間的代理服務器,進行資源在文件級別下的緩存與操控,攔截頁面請求,實如今不一樣的狀況下對不一樣請求的響應策略。網絡
首先,Service Worker本質上就是一個Web Worker,所以它具備Web Worker的特色:app
1.沒法操做DOM; 2.脫離主線程; 3.獨立上下文;
除此以外,Service Worker還具備如下特色:異步
4.只能在Https下使用; 5.運行在瀏覽器後臺,不受頁面刷新影響; 6.更強大的離線緩存能力(使用Cache API); 7.請求攔截能力; 8.徹底異步,不能使用同步API; 9.持續運行,第一次訪問頁面後,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文件同級範圍下的全部文件。函數
完成註冊後,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纔會進入到激活狀態。
//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進入激活狀態。
//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; }); } }) ) });
做爲一個PWA的初學者,我也僅僅是把現階段學到的部分總結在這裏,還有不少的問題和狀況都沒有想到、遇到。這些等之後再慢慢的補充。文章或者代碼中確定也有許多講的不合適、不規範的地方,若是有大佬看到了,還望提點。