PWA(Progressive Web Apps)是谷歌近幾年一向在推動的 web 運用新模子。PWA 藉助 Service Worker 緩存網站的靜態資本,以至是收集要求,使網站在離線時也能接見。而且我們可以為網站指定一個圖標增加在手機桌面,完成點擊桌面圖標即可接見網站。
Web App Manifest
Web App Manifest
是一個 JSON
文件,它用來定義網站增加到桌面的圖標以及從桌面圖標進入網站時的一系列行動,如:啟動款式,全屏主題等。
先建立 manifest.json
:
{
"name": "blog-pwa",
"short_name": "blog-pwa",
"icons": [
{
"src": "/img/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/img/icons/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "/index.html",
"display": "standalone",
"background_color": "#000000",
"theme_color": "#4DBA87"
}
將文件引入:
<link rel=manifest href=/manifest.json>
我們可以從開發者東西上看我們的設置:
icons
屬性定義了增加到桌面的圖標, display: standalone
示意我們要從桌面全屏啟動,theme_color": "#4DBA87
是全屏啟動時手機頂部狀態欄的背景色,background_color": "#000000
是啟動頁的背景色,啟動頁如今不能定製,默許由 background_color
加 icon
加 name
組合而成。
Web App Manifest
很簡樸,只需照着文檔每一個屬性看一遍就行。
Service Worker
Service Worker
是瀏覽器在後台獨立於網頁運轉的劇本。是它讓 PWA 具有極快的接見速率和離線運轉才。
那它是怎樣做到的呢?我們一步步來看。
註冊 Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('/service-worker.js')
.then(registration => {
console.log(
'ServiceWorker registration successful with scope: ',
registration.scope
)
})
.catch(err => {
console.log('ServiceWorker registration failed: ', err)
})
}
須要注重的是,Service Worker
劇本除了域名為 localhost
時能運轉在 http
協定下之外,只能運轉 https
協定下。
裝置
const CACHE_NAME = 'cache-v1'
const DATA_CACHE_NAME = 'data-cache-v1'
const PRE_CACHE = ['/index.html', '/css/app.css', '/js/app.js']
self.addEventListener('install', e => {
console.log('[ServiceWorker] Install')
e.waitUntil(
caches.open(CACHE_NAME).then(cache => {
return cache.addAll(PRE_CACHE)
})
)
})
在裝置的時刻預緩存網站的靜態資本,任何資本途徑失足都邑形成 Service Worker
裝置失利。
代辦要求
self.addEventListener('fetch', e => {
e.respondWith(
caches.match(e.request).then(response => {
if (response) {
return response
}
const fetchRequest = e.request.clone()
return fetch(fetchRequest).then(response => {
// Check if we received a valid response
if (!response || response.status !== 200) {
return response
}
const responseToCache = response.clone()
caches.open(DATA_CACHE_NAME).then(cache => {
cache.put(e.request, responseToCache)
})
return response
})
})
)
})
裝置勝利后,Service Worker
就可以監聽網站的一切要求,婚配到緩存時直接返回,未婚配到時要求效勞器,效勞器勝利返回時增加到緩存。
更新
如今網站的 Service Worker
已可以一般事情了,那怎樣更新它呢?
我們只須要修正 Service Worker
文件就可以更新它。當我們每次接見網站時都邑去下載這個文件,當發明文件不一致時,就會裝置這個新 Service Worker
,裝置勝利后,它將進入守候階段。當我們封閉窗口從新導航到網站時(革新網頁不可),新 Service Worker
將最先掌握網站。舊 Service Worker
停止事情並觸發 activate
事宜:
self.addEventListener('activate', e => {
e.waitUntil(
caches.keys().then(keyList => {
return Promise.all(
keyList.map(key => {
if (key !== CACHE_NAME && key !== DATA_CACHE_NAME) {
console.log('[ServiceWorker] Removing old cache', key)
return caches.delete(key)
}
})
)
})
)
})
在其卸載時一定要刪除舊緩存,不然我們的網站永久沒法更新。
上面只簡樸講了 Service Worker
怎樣事情。我們會發明有許多題目須要我們進一步處理:
- 預緩存的靜態資本修正後鄙人一次發版本時的文件名都不一樣,手動寫死太低效,最好每次都自動天生資本文件名。
- 緩存資本是以硬編碼字符串推斷是不是有用,如許每次發版本都須要手動修正,才更新緩存。而且每次都是全量更新。可否以文件的粒度舉行資本緩存呢?
- 要求代辦沒有辨別靜態資本和動態接口。已緩存的動態接口也會一向返回緩存,沒法要求新數據。
上面只列出了三個顯著的題目,另有許多題目是沒有考慮到的。假如讓我們本身來處理這些題目,不僅是事情量很大,而且也很難寫出臨盆環境可用的 Service Worker
。
workbox
既然如此,我們最好是站在偉人的肩膀上,這個偉人就是谷歌。workbox 是由谷歌瀏覽器團隊宣布,用來輔佐建立 PWA 運用的 JavaScript
庫。固然直接用 workbox
照樣太龐雜了,谷歌還很知心的宣布了一個 webpack
插件,可以自動天生 Service Worker
和 靜態資本列表 – workbox-webpack-plugin。
只需簡樸一步就可以天生臨盆環境可用的 Service Worker
:
const { GenerateSW } = require('workbox-webpack-plugin')
new GenerateSW()
打包一下:
還能說什麼呢?谷歌大法好!固然這隻是最簡樸的可用版本,實在這裡有一個最嚴峻的題目不知道有沒人發明,那就是 importScripts
援用的是谷歌域名下的 cdn ,這讓我們牆內的網站怎樣用,所以我們須要把這個題目處理並自定義一些設置加強 Service Worker
的才:
new GenerateSW({
importWorkboxFrom: 'local',
skipWaiting: true,
clientsClaim: true,
runtimeCaching: [
{
// To match cross-origin requests, use a RegExp that matches
// the start of the origin:
urlPattern: new RegExp('^https://api'),
handler: 'staleWhileRevalidate',
options: {
// Configure which responses are considered cacheable.
cacheableResponse: {
statuses: [200]
}
}
},
{
urlPattern: new RegExp('^https://cdn'),
// Apply a network-first strategy.
handler: 'networkFirst',
options: {
// Fall back to the cache after 2 seconds.
networkTimeoutSeconds: 2,
cacheableResponse: {
statuses: [200]
}
}
}
]
})
起首 importWorkboxFrom
我們指定從當地引入,如許插件就會將 workbox
一切源文件下載到當地,牆內開發者的福音。上面提到過新 Service Worker
裝置勝利后須要進入守候階段,skipWaiting: true
將使其跳過守候,裝置勝利后馬上接受網站,注重這個要和 clientsClaim
一同設置為 true
。runtimeCaching
望文生義是設置運轉時怎樣緩存要求的,這裏只說一點,緩存跨域要求時 urlPattern
的值必需為 ^
開首的正則表達式,別的的設置看文檔都能獲得細緻的引見。
再打包一次:
如今我們就可以將打包好的代碼布置到網站上了,源碼在這,末了再上幾張圖: