PWA初次體驗
前言:本示例不用安裝任何東西
部分資源來自網路資源及PWA官網,不要把PWA想象的太複雜,跟著示例走一下,你行的。
PWA介紹
一個新的前端技術,PWA( 全稱:Progressive Web App )也就是說這是個漸進式的網頁應用程式。
官網:developers.google.com/web/progres…
是 Google 在 2015 年提出,2016年6月才推廣的專案。是結合了一系列現代Web技術的組合,在網頁應用中實現和原生應用相近的使用者體驗。
官網上給出 PWA 的宣傳是 : Reliable ( 可靠的 )、Fast( 快速的 )、Engaging( 可參與的 )
Reliable :當使用者從手機主螢幕啟動時,不用考慮網路的狀態是如何,都可以立刻載入出 PWA。
Fast:這一點應該都很熟悉了吧,站在使用者的角度來考慮,如果一個網頁載入速度有點長的話,那麼我們會放棄瀏覽該網站,所以 PWA 在這一點上做的很好,他的載入速度是很快的。
Engaging : PWA 可以新增在使用者的主螢幕上,不用從應用商店進行下載,他們通過網路應用程式 Manifest file 提供類似於 APP 的使用體驗( 在 Android 上可以設定全屏顯示哦,由於 Safari 支援度的問題,所以在 IOS 上並不可以 ),並且還能進行 ”推送通知” 。
PWA關鍵技術
- Service Worker (可以理解為服務工廠)
- Manifest (應用清單)
- Push Notification(推送通知)
Service Worker
以下用SW來表示
SW 是什麼呢?這個是離線快取檔案。我們 PWA 技術使用的就是它!SW 是瀏覽器在後臺獨立於網頁執行的指令碼,它開啟了通向不需要網頁或使用者互動的功能的大門,因為使用了它,才會有的那個 Reliable 特性吧,SW 作用於 瀏覽器於伺服器之間,相當於一個代理伺服器。
瀏覽器支援
順便帶一句:目前只能在 HTTPS 環境下才能使用SW,因為SW 的權利比較大,能夠直接擷取和返回使用者的請求,所以要考慮一下安全性問題。
事件機制
功能(還是比較逆天的)
- 後臺資料的同步
- 從其他域獲取資源請求
- 接受計算密集型資料的更新,多頁面共享該資料
- 客戶端編譯與依賴管理
- 後端服務的hook機制
- 根據URL模式,自定義模板
- 效能優化
- 訊息推送
- 定時預設更新
- 地理圍欄
生命週期
-
Parsed ( 解析成功 ): 首次註冊 SW 時,瀏覽器解決指令碼並獲得入口點,如果解析成功,就可以訪問到 SW 註冊物件,在這一點中我們需要在 HTML 頁面中新增一個判斷,判斷該瀏覽器是否支援 SW 。
-
Installing ( 正在安裝 ):SW 指令碼解析完成之後,瀏覽器會嘗試進行安裝,installing 中 install 事件被執行,如果其中有 event.waitUntil ( ) 方法,則 installing 事件會一直等到該方法中的 Promise 完成之後才會成功,如果 Promise 被拒絕,則安裝失敗,SW會進入 Redundant( 廢棄 )狀態。
-
Installed / Waiting (安裝成功/等待中):如果安裝成功,SW 將會進入這個狀態。
-
Activating ( 正在啟用 ):處於 waiting 狀態的 SW 發生以下情況,將會進入 activating 狀態中:
當前已無啟用狀態的 worker 、 SW指令碼中的 self.skipWaiting()方法被呼叫 ( ps: self 是 SW 中作用於全域性的物件,這個方法根據英文翻譯過來也能明白什麼意思啦,跳過等待狀態 )、使用者已關閉 SW 作用域下的所有頁面,從而釋放了當前處於啟用狀態的 worker、超出指定時間,從而釋放當前處於啟用狀態的 worker
-
Activated ( 啟用成功 ):該狀態,其成功接收了 document 全面控制的啟用態 worker 。
-
Redundant ( 廢棄 ):這個狀態的出現時有原因的,如果 installing 事件失敗或者 activating 事件失敗或者新的 SW 替換其成為啟用態 worker 。installing 事件失敗和 activating 事件失敗的資訊我們可以在 Chrome 瀏覽器的 DevTools 中檢視
一個很不錯的全面介紹sw的教程:www.villainhr.com/page/2017/0…
Manifest
Web App Manifest 是一個 W3C 規範,它定義了一個基於 JSON 的 List 。Manifest 在 PWA 中的作用有:
能夠將你瀏覽的網頁新增到你的手機螢幕上
在 Android 上能夠全屏啟動,不顯示位址列 ( 由於 Iphone 手機的瀏覽器是 Safari ,所以不支援哦)
控制螢幕 橫屏 / 豎屏 展示
定義啟動畫面
可以設定你的應用啟動是從主螢幕啟動還是從 URL 啟動
可以設定你新增螢幕上的應用程式圖示、名字、圖示大小
Push Notification
Push 和 Notification 是兩個不同的功能,涉及到兩個 API 。
Notification 是瀏覽器發出的通知訊息。
Push 和 Notification 的關係,Push:伺服器端將更新的資訊傳遞給 SW ,Notification: SW 將更新的資訊推送給使用者。
PWA示例
準備
我們先建立一個關於 PWA 的專案資料夾,
進入資料夾下我們準備一張 120x120的圖片一張,作為我們的應用程式圖示。
建立一個 index.html 檔案
建立一個 main.css 檔案
建立一個 manifest.json 檔案
建立一個 sw.js 檔案
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello PWA</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="main.css">
<link rel="manifest" href="manifest.json">
</head>
<body>
<h3>Hello PWA</h3>
</body>
<script>
// 檢測瀏覽器是否支援SW
if(navigator.serviceWorker != null){
navigator.serviceWorker.register('sw.js')
.then(function(registartion){
console.log('支援sw:',registartion.scope)
})
}
</script>
</html>
複製程式碼
main.css
h3{
color: #f00;
}
複製程式碼
manifest.json
short_name: “ " 使用者主螢幕上的應用名字
display : “standalone" 設定啟動樣式,讓您的網路應用隱藏瀏覽器的 URL 位址列
start_url : “/“ 設定啟動網址,如果不提供的話,預設是使用當前頁面
theme_color : “ “ 用來告知瀏覽器用什麼顏色來為位址列等 UI 元素著色
background_color: “ ” 設定啟動頁面的背景顏色
icons:”” 就是新增到主螢幕之後的圖示
{
"name": "一個PWA示例",
"short_name": "PWA示例",
"start_url": "/index.html",
"display": "standalone",
"background_color": "#fff",
"theme_color": "#3eaf7c",
"icons": [
{
"src": "/youhun.jpg",
"sizes": "120x120",
"type": "image/png"
}
],
}
複製程式碼
sw.js
看網上很多人都安裝的hs和ngrok去除錯,在這裡為了照顧新手我是直接引用的sw
處理靜態快取,首先定義需要快取的路徑,以及需要快取的靜態檔案的列表。
藉助 SW 註冊完成安裝 SW 時,抓取資源寫入快取中。使用了一個方法那就是 self.skipWaiting( ) ,為了在頁面更新的過程當中,新的 SW 指令碼能夠立刻啟用和生效。
importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.1.0/workbox-sw.js");
var cacheStorageKey = 'minimal-pwa-1'
var cacheList=[
'/',
'index.html',
'main.css',
'youhun.jpg'
]
self.addEventListener('install',e =>{
e.waitUntil(
caches.open(cacheStorageKey)
.then(cache => cache.addAll(cacheList))
.then(() => self.skipWaiting())
)
})
複製程式碼
處理動態快取,我們監聽 fetch 事件,在 caches 中去 match 事件的 request ,如果 response 不為空的話就返回 response ,最後返回 fetch 請求,在 fetch 事件中我們可以手動生成 response 返回給頁面。
更新靜態資源,快取的資源會跟隨著版本的更新會過期的,所以會根據快取的字串名稱清除舊快取。在新安裝的 SW 中通過呼叫 self.clients.claim( ) 取得頁面的控制權,這樣之後開啟頁面都會使用版本更新的快取。舊的 SW 指令碼不在控制著頁面之後會被停止,也就是會進入 Redundant 期。
self.addEventListener('fetch',function(e){
e.respondWith(
caches.match(e.request).then(function(response){
if(response != null){
return response
}
return fetch(e.request.url)
})
)
})
self.addEventListener('activate',function(e){
e.waitUntil(
//獲取所有cache名稱
caches.keys().then(cacheNames => {
return Promise.all(
// 獲取所有不同於當前版本名稱cache下的內容
cacheNames.filter(cacheNames => {
return cacheNames !== cacheStorageKey
}).map(cacheNames => {
return caches.delete(cacheNames)
})
)
}).then(() => {
return self.clients.claim()
})
)
})
複製程式碼
部署
我們可以把當前pwa目錄的所有內容都扔進伺服器中,或者coding Pages和gitHub Pages也是可以的,當然,記得開啟https。在上邊介紹過SW的權利比較大,為了安全性,我們使用https協議來訪問。
試著訪問一下,我們這裡用的coding Pages並且繫結了自己的域名
開啟 chrom 的除錯工具,開啟 application ,點選 service workers 之後我們會發現 sw.js 指令碼已經存到了 SW 中 。
我們開啟 Network 重新整理頁面一下,看看,我們的頁面資源來自 SW 而不是其他的地方,在 Console 中也列印出了我們在 index.html 中判斷的語句,瀏覽器支援就會列印出這一句話。
接下來我們斷網操作,在 Application 中給 Offline 打上對勾就行啦。然後重新整理頁面,我們仍然能看到之前的頁面,原因就是我們在上圖看到,他的資源是從 SW 上獲得到的。當我們第一次開啟這個頁面的時候,Resopnse 物件被存到了 Cache Storage ( 定義在 SW 規範中 ,相關資料請同學們自行查詢啦 )中,我們看下圖:
通過存放到 Cache Storage 中,我們下次訪問的時候如果是弱網或者斷網的情況下,就可以不走網路請求,而直接就能將本地快取的內容展示給使用者,優化使用者的弱網及斷網體驗。
這個時候肯定會有同學在想,如果內容更新了,那麼頁面展示的內容是新內容呢還是舊內容呢?下面我們操作一下,開啟 index.html 檔案,我們在 body 中新增一個 p 標籤 ,然後回到頁面重新整理。
我們看到,頁面上的內容並沒有顯示出我剛剛新增的那個 p 標籤。這說明了,我們拿到的資料還是從 Cache Storage 中獲取到的,Cache Storage中的內容並沒有更新,強制重新整理也不行哦,那麼我們怎麼才能讓我剛剛新增的那個 p 標籤顯示出來呢。
我們開啟 sw.js 指令碼檔案,我們修改一下 cacheStorageKey。
修改後,我們再次開啟該網址,強制重新整理下或者關掉瀏覽器重新開啟。
頁面中出現了剛剛新增的P標籤,我們再看一下 Cache Storage 中的快取名字,已經被修改。
總結
如果是使用coding或者gitHub提供的pages服務,則需要注意最好繫結下獨立域名。如果不繫結則注意下檔案請求路徑即可。
研究PWA門檻不低,部署的伺服器要求HTTPS,ServiceWorker涉及API眾多,需要單獨學習,另外npm中也已經有這個包了https://www.npmjs.com/package/web-pwa ,玩玩可以,真正部署到專案生產環境可能坑很多,但有坑填坑,不折騰還叫前端麼。