Progressive Web App, 簡稱 PWA,是提高Web App
的體驗的一種新方法,能給用戶原生應用的體驗,致力於用前沿的技術開發,讓網頁使用如同原生App般的體驗的一系列方案。javascript
用來自Google Developers的解答Progressive Web Apps
:css
Service Worker
可以在離線或者網絡較差的狀況下正常訪問App Shell
模型基礎上開發,由於應具備 Native App
的交互和導航,給用戶 Native App
的體驗HTTPS
協議提供服務,防止窺探和確保內容不被篡改Service Worker
可讓搜索引擎索引到,從而將其識別爲『應用』咱們也能夠經過一個DEMO看看實際效果=>天氣 PWAhtml
而其中,PWA方案的最主要核心功能都是依賴於Service Worker
這個API來實現的.前端
W3C 組織早在 2014 年 5 月就提出過 Service Worker
這樣的一個 HTML5 API ,主要用來作持久的離線緩存。html5
固然這個 API 不是憑空而來,至於其中的由來咱們能夠簡單的捋一捋:java
瀏覽器中的javaScript
都是運行在一個單一主線程上的,在同一時間內只能作一件事情。隨着 Web 業務不斷複雜,咱們逐漸在 js 中加了不少耗資源、耗時間的複雜運算過程,這些過程致使的性能問題在 WebApp 的複雜化過程當中更加凸顯出來。webpack
W3C 組織早早的洞察到了這些問題可能會形成的影響,這個時候有個叫Web Worker
的 API 被造出來了,這個 API 的惟一目的就是解放主線程,Web Worker
是脫離在主線程以外的,將一些複雜的耗時的活交給它幹,完成後經過 postMessage
方法告訴主線程,而主線程經過 onMessage
方法獲得 Web Worker
的結果反饋。git
一切問題好像是解決了,但 Web Worker 是臨時(即瀏覽器關閉後就關閉了)的,咱們能不能有一個東東是一直持久存在的,而且隨時準備接受主線程的命令呢?基於這樣的需求推出了最第一版本的 Service Worker
,Service Worker
在 Web Worker
的基礎上加上了持久離線緩存能力.github
Service Worker
有如下功能和特性:web
因此咱們基本上知道了 Service Worker
的偉大使命,就是讓緩存作到優雅和極致,讓 Web App 相對於 Native App 的缺點更加弱化,也爲開發者提供了對性能和體驗的無限遐想。
Service Worker
的技術核心是Service Worker
腳本,它 是一種由Javascript
編寫的瀏覽器端代理腳本。
前端頁面向內核發起註冊時會將腳本地址通知內核,內核會啓動獨立進/線程加載Service Worker
腳本並執行Service Worker
安裝及激活動做。成功激活後便進入空閒等待狀態,若當前的Service Worker
進/線程一直沒有管轄的頁面或者事件消息時會自動終止(具體的終止策略視不一樣瀏覽器及版本而定,不會影響前端編寫邏輯,但前端勿在Service Worker
腳本中保存須要持久化的信息,能夠藉助localstorage
),當打開新的可管轄頁面或者已管轄頁面發起message
等消息時,Service Worker
進/線程會被從新喚起。
每當已安裝的Service Worker
有管轄頁面被打開時,便會觸發Service Worker
腳本更新,當Service Worker
腳本發生了更改,便會忽略本地網絡cache
的Service Worker
腳本直接從網絡拉取。若網絡拉取的與本地有一個字節的差別都會觸發Service Worker
腳本的更新,更新流程與安裝相似,只是在更新安裝成功後不會當即進入active
狀態,須要等待舊版本的Service Worker
進/線程終止。
// 在html裏註冊service-worker
if (navigator.serviceWorker != null) {
navigator.serviceWorker.register('sw.js')
.then(function(registration) {
console.log('Registered events at scope: ', registration.scope);
});
}
複製代碼
// 首先定義須要緩存的路徑, 以及須要緩存的靜態文件的列表。
var cacheStorageKey = 'minimal-pwa-8'
var cacheList = [
'/',
"index.html",
"main.css",
"e.png",
"*.png"
]
// 藉助 Service Worker, 能夠在註冊完成安裝 Service Worker 時, 抓取資源寫入緩存:
window.addEventListener('install', function(e) {
console.log('Cache event!')
e.waitUntil(
caches.open(cacheStorageKey).then(function(cache) {
console.log('Adding to Cache:', cacheList)
return cache.addAll(cacheList)
}).then(function() {
console.log('Skip waiting!')
return self.skipWaiting()
})
)
})
// 網頁抓取資源的過程當中, 在 Service Worker 能夠捕獲到 fetch 事件, 能夠編寫代碼決定如何響應資源的請求:
window.addEventListener('fetch', function(e) {
// console.log('Fetch event:', e.request.url)
e.respondWith(
caches.match(e.request).then(function(response) {
if (response != null) {
console.log('Using cache for:', e.request.url)
return response
}
console.log('Fallback to fetch:', e.request.url)
return fetch(e.request.url)
})
)
})
複製代碼
install 事件:當前Service Worker
腳本被安裝時,會觸發 install 事件。
push事件: push 事件是爲推送通知而準備的。不過首先你須要瞭解一下 Notification API
和 PUSH API
。
經過 PUSH API
,當訂閱了推送服務後,可使用推送方式喚醒 Service Worker
以響應來自系統消息傳遞服務的消息,即便用戶已經關閉了頁面。
online/offline事件: 當網絡狀態發生變化時,會觸發 online
或 offline
事件。結合這兩個事件,能夠與 Service Worker
結合實現更好的離線使用體驗,例如當網絡發生改變時,替換/隱藏須要在線狀態才能使用的連接導航等。
fetch 事件: 當咱們安裝完Service Worker
成功並進入激活狀態後即運行於瀏覽器後臺,咱們的這個線程就會一直監控咱們的頁面應用,若是出現HTTP
請求,那麼就會觸發fetch
事件,而且給出本身的響應。 這個功能是十分強大的,藉助 Fetch API
和 Cache API
能夠編寫出複雜的策略用來區分不一樣類型或者頁面的資源的處理方式。它可以提供更加好的用戶體驗: 例如能夠實現緩存優先、降級處理的策略邏輯:監控全部 http 請求,當請求資源已經在緩存裏了,直接返回緩存裏的內容;不然使用 fetch API 繼續請求,若是是 圖片或 css、js 資源,請求成功後將他們加入緩存中;若是是離線狀態或請求出錯,則降級返回預緩存的離線內容。
看到這裏不少人會有疑問了,既然能夠經過service-worker
緩存資源,那若是一個正式項目,在項目迭代後,並將代碼推送到正式環境後,前端怎麼實時知道並從新緩存新的資源呢?
第一種方式,就是每次修改都手動去更改sw文件的版本號,觸發更新。
第二種就是使用webpack
插件自動化處理
事實上,在咱們真實的用webpack
生成的項目中,若是按照第一種方式手動去寫Service-worker.js
文件的話,會遇到兩個問題:
webpack
生成的資源多會生成一串hash,Service-worker.js
的資源列表裏面須要同步更新這些帶hash的資源;service-worker
文件版本號來通知客戶端對所緩存的資源進行更新.看到這裏就該讓用webpack
插件:offline-plugin 登場了,官方同時也推薦sw-precache-webpack-plugin ,offline-plugin不只可以解決剛剛那個提到的緩存更新的問題,同時還具有如下的優勢:
manifest
文件。部署到項目中也十分的簡單
npm install offline-plugin [--save-dev]
複製代碼
第一步,進入webpack.config.js
:
// webpack.config.js example
var OfflinePlugin = require('offline-plugin');
module.exports = {
// ...
plugins: [
// ... other plugins
// it's always better if OfflinePlugin is the last plugin added
new OfflinePlugin()
]
// ...
}
複製代碼
import * as OfflinePluginRuntime from 'offline-plugin/runtime';
OfflinePluginRuntime.install();
複製代碼
通過上面的步驟,offline-plugin
已經集成到項目之中,經過webpack
構建便可。
具體代碼也可查看 demo
畢竟是自家產品,chrome瀏覽器確定是支持度最高的瀏覽器,chrome64版本是基本支持全部PWA功能API
的。
但國內的瀏覽器·支持狀況相對差一些,並且chrome移動版的使用人羣仍是偏少的,不過在UC的支持程度也不低