什么是PWA?
筆記內容來自:https://segmentfault.com/a/1190000012353473
PWA全稱Progressive Web App,即漸進式web應用。
一個PWA應用首先是一個網頁,可以通過Web技術編寫出一個網頁應用。隨后添加上App Manifest和Service Worker來實現PWA的安裝和離線等功能
解決了哪些問題?
- 可以添加至主屏幕,點擊主屏幕圖標可以實現啟動動畫以及隱藏地址欄
- 實現離線緩存功能,即時用戶手機沒有網絡,依然可以使用一些離線功能
- 實現了消息推送
它解決了上述提到的問題,這些特性將使得Web應用漸進式接近原生APP
PWA的實現
Manifest實現添加至主屏幕
index.html
<head> <title>Minimal PWA</title> <meta name="viewport" content="width=device-width, user-scalable=no" /> <link rel="manifest" href="manifest.json" /> <link rel="stylesheet" type="text/css" href="main.css" /> <link rel="icon" href="/e.png" type="image/png" /> </head>
manifest.json
{ "name": "Minimal PWA", "short_name": "PWA Demo", "description": "The app that helps you understand PWA", "display": "standalone", "start_url": "/", "theme_color": "#313131", "background_color": "#313131", "icons": [ { "src": "icon/lowres.webp", "sizes": "48x48", "type": "image/webp" }, { "src": "icon/lowres", "sizes": "48x48" }, { "src": "icon/hd_hi.ico", "sizes": "72x72 96x96 128x128 256x256" }, { "src": "icon/hd_hi.svg", "sizes": "72x72" } ] }
Manifest參考文檔:https://developer.mozilla.org/zh-CN/docs/Web/Manifest
Service worker實現離線緩存
什么是service worker?
Service Worker是Chrome團隊提出和力推的一個Web Api,用於給web應用提供高級的可持續的后台處理能力
Service Workers就像介於服務器和網頁之間的攔截器,能夠攔截進出的HTTP請求,從而完全控制你的網站。
最主要的特點
- 在頁面中注冊並安裝成功后,運行於瀏覽器后台,不受頁面刷新的影響,可以監聽和攔截作用域范圍內所有頁面的HTTP請求。
- 網站必須使用HTTPS。除了使用本地開發環境調試時(如域名使用localhost)
- 運行於瀏覽器后台,可以控制打開的作用域范圍下所有的頁面請求
- 單獨的作用域范圍,單獨的運行環境和執行線程
- 不能操作頁面DOM,但可以通過事件機制來處理
- 事件驅動型服務線程
為什么要求網站必須是HTTPS的,大概是因為service worker權限太大,能攔截所有頁面的請求吧,如果http的網站安裝service worker很容易被攻擊
生命周期
當用戶首次導航至URL時,服務器會返回響應的網頁。
第一步:當你調用register()函數時,Service Worker開始下載
第二步: 在注冊過程中,瀏覽器會下載、解析並執行Service Worker()。如果在此步驟中出現任何錯誤,register()返回的promise都會執行reject操作,並且Service Worker會被廢棄。
第三步: 一旦Service Worker成功執行了,install事件就會激活
第四步: 安裝完成,Service Worker便會激活,並控制在其范圍內的一切。如果生命周期中的所有事件都成功了,Service Worker便已准備就緒,隨時可以使用了。
HTTP緩存與service worker緩存
HTTP緩存
Web服務器可以使用Expires首部來通知Web客戶端,它可以使用資源的當前副本,直到指定的“過期時間”。反過來,瀏覽器可以緩存此資源,並且只有在有效期滿后才會再次檢查新版本。
使用HTTP緩存意味着你要依賴服務器來告訴你何時緩存資源和何時過期。
service worker緩存
Service Worker的強大在於它們攔截HTTP請求的能力
進入任何傳入的HTTP請求,並決定想要如何響應。在你的Service Worker中,可以編寫邏輯來決定想要緩存的資源,以及需要滿足什么條件和資源需要緩存多久。一切盡歸你掌控!
實現離線緩存
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Hello Caching World</title> </head> <body> <img src="/images/hello.png" alt=""> <script async src="/js/script.js"></script> <script> // 注冊 service worker if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js', {scope: '/'}).then(function(registration) { // 注冊成功 console.log('ServiceWorker registration successful with scope: ', registration.scope); }).catch(function(err){ // 注冊失敗 console.log('ServiceWorker registration failed: ', err) }) } </script> </body> </html>
注:Service Worker的注冊路徑決定了其scope默認作用頁面的范圍
如果service-worker.js是在/sw/頁面路徑下,這使得該Service Worker默認只會收到頁面/sw/路徑下的fetch事件。
如果存放在網站的根路徑下,則將會收到該網站的所有fetch事件。
如果希望改變它的作用域,可在第二個參數設置scope范圍。示例中將其改為了根目錄,即對整個站點生效。
service-worker.js
var cacheName = 'helloWorld'; // 緩存的名稱 // install事件,它發生在瀏覽器安裝並注冊Service Worker時 self.addEventListener('install', event => { /** event.waitUntil用於在安裝成功之前執行一些預裝邏輯 * 但是建議只做一些輕量級和非常重要資源的緩存,減少安裝失敗的概率 * 安裝成功后 ServiceWorker 狀態會從installing變為installed */ event.waitUntil( caches.open(cacheName) .then(cache => cache.addAll([ // 如果所有的文件都成功緩存了,便會安裝完成。如果任何文件下載失敗了,那么安裝過程也會隨之失敗。 '/js/script.js', '/images/hello.png' ])) ) }) /** * 為fetch事件添加一個事件監聽器。接下來,使用caches.match()函數來檢查傳入的請求URL是否匹配當前緩存中存在的任何內容。如果存在的話,返回緩存的資源. * 如果資源並不存在於緩存當中,通過網絡來獲取資源,並將獲取到的資源添加到緩存中。 */ self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request).then(function(response) { if (response) { return response; } var requestToCache = event.request.clone(); return fetch(requestToCache).then( function(response) { if (!response || response.status !== 200) { return response; } var responseToCache = response.clone(); caches.open(cacheName).then(function (cache) { cache.put(requestToCache, responseToCache); }); return response; } ) }) ) })
注:為什么用request.clone()和response.clone()?
因為request和response是一個流,它只能消耗一次。我們已經通過緩存消耗了一次,然后發起HTTP請求還要再消耗一次,所以我們需要在此時克隆請求
service worker實現消息推送
- 提示用戶並獲得他們的訂閱詳細信息
- 將這些詳細信息保存在服務器上
- 在需要時發送任何消息
不同瀏覽器需要用不同的推送消息服務器。以Chrome上使用Google Cloud Messaging<GCM>作為推送服務為例,第一步是注冊applicationServerKey(通過GCM注冊獲取),並在頁面上進行訂閱或發起訂閱。每一個會話會有一個獨立的端點,訂閱對象的屬性即為端點值。將端點發送給服務器后,服務器用這一值來發送消息給會話的激活的Service Worker(通過GCM與瀏覽器客戶端溝通)
PWA的優勢
- 可以將app的快捷方式放置到桌面上,全屏運行,與原生app無異
- 能夠在各種網絡環境下使用,包括網絡差和斷網條件下,不會顯示undefined
- 推送消息的能力
- 其本質是一個網頁,沒有原生app的各種啟動條件,快速響應用戶指令
PWA存在的問題
- 支持率不高,現在ios手機端不支持pwa,IE也暫時不支持
- Chrome在中國桌面版占有率還是不錯的,安卓移動端上的占有率卻很低。
- 各大廠商還未明確支持pwa
- 依賴的GCM服務在國內無法使用
- 微信小程序的競爭
盡管有上述的一些缺點,PWA技術仍然有很多可以使用的點。
- service worker技術實現離線緩存,可以將一些不經常更改的靜態文件放到緩存中,提升用戶體驗
- service worker實現消息推送,使用瀏覽器推送功能,吸引用戶
- 漸進式開發,盡管一些瀏覽器暫時不支持,可以利用上述技術給使用支持瀏覽器的用戶帶來更好的體驗。