首先了解一下PWA(Progressive web apps,漸進式 Web 應用)
運用現代的 Web API
以及傳統的漸進式加強策略來建立跨平臺 Web
應用程序。 PWA的優勢 PWA
是可被發現、易安裝、可連接、獨立於網絡、漸進式、可重用、響應性和安全的。 PWA
中能夠經過Service Worker
來實現離線的應用,這個也是PWA中一個比較重要的環節,它們主要應用到Web App中,已得到更好的體驗,而且在如今也在大規模的應用。 Service Worker
是一個事件驅動worker
,運行在一個單獨的後臺進程,是PWA(ProgressiveWeb App)
運行的基礎。主要用於代理網頁請求,可緩存請求結果;可實現離線緩存功能,也擁有單獨的做用域範圍和運行環境。咱們之後把Service Worker
簡稱爲SW
。javascript
本文大體分爲:css
實例代碼實例代碼地址html
它們的運行在一個與咱們頁面的 JavaScript 主線程獨立的線程上
,而且沒有對 DOM 結構
的任何訪問權限。 這引入了與傳統 Web 編程不一樣的方法 - API 是非阻塞的
,而且能夠在不一樣的上下文之間發送和接收信息
。前端
SW
除了work
線程的限制外,因爲可攔截頁面請求,爲了保證頁面安全,瀏覽器端對sw
的使用限制也很多。java
window
、document
、parent
對象。能夠訪問navigator
、location
; SW
經過響應 postMessage
接口發送的消息來與其控制的頁面通訊,頁面可在必要時對 DOM
執行操做。sw.js
所在文件目錄及子目錄的請求可代理,可在註冊時手動設置做用域範圍;https
中使用,容許在開發調試的localhost
使用。能夠經過查詢service worker能夠看到他在不一樣平臺或不一樣瀏覽器中的兼容性。git
SW
的生命週期徹底獨立於網頁。 SW
爲網頁添加一個相似於 App
的生命週期,它只會響應系統事件,就算瀏覽器關閉時操做系統也能夠喚醒 SW
,這點很是重要,讓Web App
與 Native App
的能力變得相似了。因爲是離線緩存,因此在初始安裝時、更新它們的所走的生命週期是不相同。下面咱們就根據這兩種場景結合代碼來分析它的執行步驟。 SW
的生命週期大體分爲:註冊、更新、安裝成功、安裝失敗、激活、銷燬。github
使用SW前提條件web
https
中使用,容許在開發調試的localhost
使用。初始安裝時大體流程大體以下圖: 正則表達式
大體能夠分爲註冊SW => 安裝SW => 激活 => 空閒 => (緩存和返回請求/終止)
,在初始安裝時會大體分爲這幾個步驟,下面就按照這幾個步結合代碼實現。chrome
用戶首次訪問SW控制的網站或頁面時,sw.js
會馬上被下載和解析。咱們要在頁面中寫入JavaScript
來註冊SW
。
// 判斷瀏覽器是否支持serviceWorker
if ('serviceWorker' in navigator) {
// 在頁面加載後
window.addEventListener('load', function () {
// 經過navigator.serviceWorker.register 註冊'./sw.js
navigator.serviceWorker.register('./sw.js')
.then(reg => { //註冊成功
console.log('註冊成功', reg)
}).catch(err => { //註冊成功
console.log('註冊失敗', err)
})
});
} else {
console.log('當前瀏覽器不支持SW')
}
複製代碼
首先檢瀏覽器是否支持SW,若是支持就在瀏覽器加載後經過register().then
註冊sw.js,而且設置註冊成功或者失敗的回調函數。
注意:
register()
方法的精妙之處在於服務工做線程文件的位置。SW
降接收此網域上全部的事項的featch
事件。 若是是Chrome瀏覽器能夠經過chrome://inspect/#service-workers
或者console => application => Service Worker
查看是否註冊成功
由於如今sw.js
中咱們的代碼是空的,因此在瀏覽器中的cache stoage
是空的,運行效果以下:
在受控頁面啓動註冊流程後,下面就是SW獲取的第一個事件install
,而且只發生一次。傳遞到 installEvent.waitUntil()
的一個 promise
可代表安裝的持續時間以及安裝是否成功。
在install
中要作三件事打開緩存、緩存文件、確認全部須要的資產是否已緩存。
// 在sw.js中監聽對應的安裝事件,並預處理須要緩存的文件
// 該部份內容涉及到cacheStorage API
// 定義緩存空間名稱
const CACHE_NAME = 'sw_cache_v1';
// 定義須要緩存的文件目錄
let cachelist = ['./app.js', './index.css'];
// 監聽安裝事件,返回installEvent對象
self.addEventListener('install', function (installEvent) {
// waitUntil方法執行緩存方法
installEvent.waitUntil(
// cacheStorage API 可直接用caches來替代
// open方法建立/打開緩存空間,並會返回promise實例
// then來接收返回的cache對象索引
caches.open(CACHE_NAME)
// cache對象addAll方法解析(同fetch)並緩存全部的文件
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(cachelist);
})
);
});
複製代碼
第一個事件爲install
,該事件在Worker
執行時當即觸發。在install
的回調函數中,咱們經過caches.open(CACHE_NAME)
打開緩存,以後調用cache.addAll()
並傳入路徑數組。這是一個promise
鏈(caches.open()
和chaches.addAll()
。installEvent.waitUntill()
放大帶有promise
並使用它來判斷安裝所花時間,以及是否安裝成功。
注意: 第一個事件
install
,它只能被每一個 SW 調用一次。若是您更改您的 SW 腳本,則瀏覽器將其視爲一個不一樣的 SW,而且它將得到本身的install
事件。 若有任何文件沒法下載,則安裝步驟將失敗。 當前的狀態是在等待狀態。 咱們能夠直接經過self.skipwaiting()
讓當前sw
當即將狀態提高到active
。
當咱們安裝成功時,效果以下圖所示:
會多了一個skipWaiting
,還有在cache stroage
中的當前域名下的service worker
對應的緩存文件列表。這個時候咱們即便刷新也不會走service worker
的緩存的。
SW
準備控制客戶端並處理 push
和 sync
等功能事件時,您將得到一個 activate
事件。但這不意味着調用 .register()
的頁面將受控制。若是第二次加載此演示(換言之,刷新頁面),該頁面將受控制。改寫代碼sw.js
以下:
self.addEventListener('install', () => {
// 通常註冊之後,激活須要等到再次刷新頁面後再激活
// 可防止出現等待的狀況,這意味着服務工做線程在安裝完後當即激活
self.skipWaiting();
})
self.addEventListener('activate', function (event) {
event.waitUntil(
// cacheStorage API 可直接用caches來替代
// open方法建立/打開緩存空間,並會返回promise實例
// then來接收返回的cache對象索引
caches.open(CACHE_NAME)
// cache對象addAll方法解析(同fetch)並緩存全部的文件
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(cachelist);
})
);
})
複製代碼
通常在書寫的時候,會在install()
註冊以後直接經過self.skipWaiting();
激活當前的SW
,在activate
中書寫打開緩存等等的邏輯,就不會出現上面還要刷新或者手動激活的問題。效果圖以下:
可是若是出現更新SW,而且更新了緩存列表或者出現異步資源時,咱們能夠經過clients.claim()
更新緩存列表。
激活 SW
後,您能夠經過在其中調用 clients.claim()
控制未受控制的客戶端。google developer中的一個異步加載圖片的實例。下面修改代碼以下:
self.addEventListener('install', (event) => {
event.waitUntil(
// cacheStorage API 可直接用caches來替代
// open方法建立/打開緩存空間,並會返回promise實例
// then來接收返回的cache對象索引
caches.open(CACHE_NAME)
// cache對象addAll方法解析(同fetch)並緩存全部的文件
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(cachelist);
})
);
// 通常註冊之後,激活須要等到再次刷新頁面後再激活
// 可防止出現等待的狀況,這意味着服務工做線程在安裝完後當即激活
self.skipWaiting();
})
self.addEventListener('activate', function (event) {
// 若緩存數據更改,則在這裏更新緩存
var cacheDeletePromise = caches.keys()
.then(keyList => {
Promise.all(keyList.map(key => {
if (key !== CACHE_NAME) {
var deletePromise = caches.delete(key)
return deletePromise
} else {
Promise.resolve()
}
}));
});
event.waitUntil(
Promise.all([cacheDeletePromise]).then(res => {
this.clients.claim()
})
);
})
複製代碼
用於處理更新緩存,新的文件。到如今咱們仍是沒有用到serviceWorker
的緩存,下面重頭戲來了緩存和返回請求。
上咱們已經安裝而且激活了SW,如今咱們要返回一個緩存的響應。SW用戶轉至其餘頁面或刷新當前頁面後,將開始接受fetch
事件。
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
return fetch(event.request);
})
);
});
複製代碼
在定義的fetch
事件中,咱們在event.respondWith()
中傳入來自caches.match()
的一個promise
。caches.match()
這個方法檢視該請求,並從服務工做線程所建立的任何緩存中查找緩存的結果。若是命中返回緩存值,不然,將調用fetch
以發出網絡請求。運行效果以下:
若是咱們想把新的請求也緩存掉,修改代碼以下:
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
// return fetch(event.request);
var requestClone = event.request.clone();
return fetch(requestClone).then(response => {
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
});
})
);
});
複製代碼
執行操做以下:
fetch
請求中添加對 .then()
的回調。沒有緩存新請求時效果以下:
當使用咱們下面的代碼時,效果圖以下:
即便異步請求的png
圖片也被加入了緩存中。
在如下狀況下會觸發更新:
push
和 sync
等功能事件,除非在前 24 小時內已進行更新檢查。.register()
,僅在 SW
網址已發生變化時。當觸發更新時,會通過大體以下步驟:
JavaScript
文件。 用戶導航至您的站點時,瀏覽器會嘗試在後臺從新下載定義 SW
的腳本文件。 若是 SW
文件與其當前所用文件存在字節差別,則將其視爲新 SW
。SW
與現有 SW
一塊兒啓動,並獲取本身的 install
事件。SW
仍控制着當前頁面,所以新 SW
將進入 waiting
狀態。新 Worker
出現不正常狀態代碼(例如,404)、解析失敗,在執行中引起錯誤或在安裝期間被拒,則系統將捨棄新 Worker
,但當前 Worker 仍處於活動狀態
。更新的 Worker 將 wait
,直到現有 Worker
控制零個客戶端。(注意,在刷新期間客戶端會重疊。)self.skipWaiting()
可防止出現等待狀況,這意味着 Service Worker
在安裝完後當即激活。更新一個叫作sw_cache_v2
的新的SW
緩存,代碼以下:
const CACHE_NAME = 'sw_cache_v2';
self.addEventListener('install', (event) => {
event.waitUntil(
// cacheStorage API 可直接用caches來替代
// open方法建立/打開緩存空間,並會返回promise實例
// then來接收返回的cache對象索引
caches.open(CACHE_NAME)
// cache對象addAll方法解析(同fetch)並緩存全部的文件
.then(function(cache) {
return cache.add('index_copy.png')
})
);
// 通常註冊之後,激活須要等到再次刷新頁面後再激活
// 可防止出現等待的狀況,這意味着服務工做線程在安裝完後當即激活
self.skipWaiting();
})
self.addEventListener('activate', function (event) {
// 若緩存數據更改,則在這裏更新緩存
var cacheDeletePromise = caches.keys()
.then(keyList => {
Promise.all(keyList.map(key => {
if (key !== CACHE_NAME) {
var deletePromise = caches.delete(key)
return deletePromise
} else {
Promise.resolve()
}
}));
});
event.waitUntil(
Promise.all([cacheDeletePromise]).then(res => {
this.clients.claim()
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
return fetch(event.request);
}
)
);
});
複製代碼
代碼執行效果以下圖所示:
整個過程咱們大體通過了install => waiting => activate
三個過程。
咱們在代碼中把sw_cache_v1
更改成sw_cache_v2
,咱們從新Install
了一個新的緩存sw_cache_v2
,而且經過添加了一個緩存進去cache.add('index_copy.png')
。
若是新的緩存安裝成功SW
後,更新的SW
將延遲激活,直到現有SW
再也不控制任何客戶端。此狀態爲waiting
,這是瀏覽器確保每次只運行一個SW
版本的樣式。
舊 SW
退出時將觸發 Activate
,新 SW
將可以控制客戶端。此時,您能夠執行在仍使用舊 Worker
時沒法執行的操做,如遷移數據庫和清除緩存。 在上面的演示中,我維護了一個指望保存的緩存列表,而且在 activate
事件中,我刪除了全部其餘緩存,從而也移除了舊的 sw_cache_v1
緩存。
不要更新之前的版本。它多是許多舊版本的
SW
。
若是您將一個 promise
傳遞到 event.waitUntil()
,它將緩衝功能事件(fetch、push、sync
等),直到 promise
進行解析。所以,當您的 fetch
事件觸發時,激活已所有完成。
Cache storage API
屬於「源存儲」(如localStorage
和IndexedDB
)。若是您在同源上運行許多網站(例如,yourname.github.io/myapp
),請注意,不要刪除其餘網站的緩存。爲避免此問題,能夠爲您的緩存名稱提供一個在當前網站上具備惟一性的前綴(例如,myapp-static-v1),而且不要刪除緩存,除非它們以 myapp- 開頭。
等待階段表示您每次只能運行一個網站版本,但若是您不須要該功能,您能夠經過調用 self.skipWaiting()
儘快將新 SW
激活。 這會致使您的 SW
將當前活動的 Worker
逐出,並在進入等待階段時儘快激活本身(或當即激活,前提是已經處於等待階段)。這不能讓您的 Worker
跳過安裝,只是跳過等待階段。 skipWaiting()
在等待期間調用仍是在以前調用並無什麼不一樣。通常狀況下是在 install
事件中調用它:
self.addEventListener('install', event => {
self.skipWaiting();
event.waitUntil(
// caching etc
);
});
複製代碼
與 clients.claim()
同樣,它是一個競態。
skipWaiting()
意味着新 Service Worker 可能會控制使用較舊Worker
加載的頁面。這意味着頁面提取的部分數據將由舊 Service Worker 處理,而新 Service Worker 處理後來提取的數據。若是這會致使問題,則不要使用skipWaiting()
。
當頁面刷新或者執行功能性事件時,瀏覽器會自動檢查更新,其實咱們也能夠手動的來觸發更新:
navigator.serviceWorker.register("/sw.js").then(reg => {
// sometime later…
reg.update();
});
複製代碼
若是你但願你的用戶訪問頁面很長時間並且不用刷新,那麼你能夠每一個一段時間調用一次update()
。
你可能會考慮給每一個 SW
不一樣的 URL
。**千萬不要這麼作!**在 SW
中這麼作是「最差實踐」,要在原地址上修改 SW
。
舉個例子來講明爲何:
index.html
註冊了sw-v1.js
做爲SW
。
sw-v1.js
對index.html
作了緩存,也就是緩存優先(offline-first
)。
你更新了index.html
從新註冊了在新地址的 SW sw-v2.js
.
若是你像上面那麼作,用戶永遠也拿不到sw-v2.js
,由於index.html
在sw-v1.js
緩存中,這樣的話,若是你想更新爲sw-v2.js
,還須要更改原來的sw-v1.js
。
這裏主要分爲:
SW
生命週期是專爲用戶構建的,這就給開發工做帶來必定的困難。幸運的是,咱們可經過如下幾個工具解決這個問題:
這可以使生命週期變得對開發者友好。每次瀏覽時都將:
SW
。install
事件並更新緩存。SW
。若是您有一個 Worker
在等待,您能夠按 DevTools
中的「skip waiting
」以當即將其提高到「active
」。同時也能夠經過self.skipWaiting()
來實現。
若是您強制從新加載頁面 (shift-reload)
,則將徹底繞過 SW
。頁面將變得不受控制。此功能已列入規範,所以,它在其餘支持 SW
的瀏覽器中也適用。
爲支持儘量多的模式,整個更新週期都是可觀察的:
navigator.serviceWorker.register('/sw.js').then(reg => {
reg.installing; // the installing worker, or undefined
reg.waiting; // the waiting worker, or undefined
reg.active; // the active worker, or undefined
reg.addEventListener('updatefound', () => {
// A wild service worker has appeared in reg.installing!
const newWorker = reg.installing;
newWorker.state;
// "installing" - the install event has fired, but not yet complete
// "installed" - install complete
// "activating" - the activate event has fired, but not yet complete
// "activated" - fully active
// "redundant" - discarded. Either failed install, or it's been
// replaced by a newer version
newWorker.addEventListener('statechange', () => {
// newWorker.state has changed
});
});
});
navigator.serviceWorker.addEventListener('controllerchange', () => {
// This fires when the service worker controlling this page
// changes, eg a new worker has skipped waiting and become
// the new active worker.
});
複製代碼
Sync事件
讓你能夠先將網絡相關任務延遲到用戶有網絡的時候再執行。這個功能常被稱做「背景同步」。這功能能夠用於保證任何用戶在離線的時候所產生對於網絡有依賴的操做,最終能夠在網絡再次可用的時候抵達它們的目標。
代碼示例以下
navigator.serviceWorker.ready.then(registration => {
document.getElementById('submit').addEventListener('click', () => {
registration.sync.register('submit').then(() => {
console.log('sync registered!');
});
});
});
複製代碼
咱們指定在一個按鈕的點擊事件裏,在一個全局的 ServiceWorkerRegistration
對象身上調用 sync.register
。
簡單地講,任何你須要確保在有網絡時馬上執行或者等到有網再執行的操做,都須要註冊爲一個sync事件
。
這操做能夠是發送一個評論,或者獲取用戶信息,在SW的事件監聽器裏會以下定義:
// sw.js
self.addEventListener('sync', event => {
if (event.tag === 'submit') {
console.log('sync!');
}
});
複製代碼
咱們監聽一個 sync 事件
,而後在 SyncEvent
對象上檢查 tag
是否匹配咱們在點擊事件裏所設定的 'submit'
。
若是多個 tag
標記爲 submit
的 sync
事件被註冊了,sync
事件處理器只會運行一次。
因此在這個例子裏,若是用戶離線了,而後點擊按鈕7次,當網絡再次連上,全部的sync
註冊都會合而爲一,sync
事件只會觸發一次。
若是用戶的網絡時聯通的,那麼sync事件
會馬上觸發而且馬上執行你所定義的任務。
而若是用戶離線了,sync 事件
會在網絡恢復後第一時間觸發。
對於在install
中發現沒有緩存,頁面又依賴但又不常常變化的資源,能夠在頁面打開或發生用戶交互時觸發fetch
而後使用fetch api
再去網絡拉取,將返回正常的response緩存
起來以便下次使用。
progressive-cache
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
// return fetch(event.request);
var requestClone = event.request.clone();
return fetch(requestClone).then(response => {
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
});
})
);
});
複製代碼
在fetch事件
中,僅去匹配資源,若匹配失敗,表現出來的就是前端頁面對於該 資源加載失敗。這裏容錯性比較差,適用於頁面資源都是靜態資源的,且不能使用不影響安裝的資源預緩存。
cache-only
// SW請求攔截事件
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.open(OFFLINE_CACHE_NAME).then((cache) => {
// 匹配資源若是命中返回緩存
return cache.macth(event.request.url);
});
);
});
複製代碼
在fetch事件
中,僅將request
從新抽出用fetch
去網絡加載並返回給前端頁面。適用於資源大可能是動態資源、實時性要求高的場景。
network-only
// SW請求攔截事件
self.addEventListener('fetch', (event) => {
// 僅使用網絡加載
event.respondWith(fetch(event.request));
});
複製代碼
簡單的資源緩存中使用的就是緩存優先策略,先去緩存匹配,匹配失敗折回網絡,這算是最經常使用、容錯性能好的一種策略。
firstCache
function firstCache (cacheName, request) {
// 打開SW
return caches.open(cacheName).then(cache => {
// 匹配請求路徑
return cache.match(request).then(response => {
// fetch請求
var fetchServer = function() {
return fetch(request).then(newResponse => {
cache.put(request, newResponse.clone());
return newResponse;
});
}
// 若是緩存中有數據則返回,不然請求網絡數據
if (response) {
return response;
} else {
return fetchServer();
}
});
});
}
複製代碼
在fetch事件
中先去網絡fetch
,當出現服務器故障或者網絡不良時,折回本地緩存,目的是爲了展現最新的數據,對實時性要求比較高但又可以帶來良好體驗的應用,好比天氣類型應用。
firstNet
function firstNet(cacheName, request) {
// 請求網絡數據並緩存
return fetch(request).then(response => {
// 響應clone
var responseCopy = response.clone();
caches.open(cacheName).then(cache => {
// fetch請求
cache.put(request, responseCopy);
});
return response;
}).catch(() => {
// fetch失敗走本地緩存
return caches.open(cacheName).then(cache => {
return cache.match(request);
});
});
}
複製代碼
在fetch事件
中同時發起本地緩存匹配及網絡請求,誰先返回使用誰的,該方案適用於對性能要求比較高的站點,縮短了緩存優先策略中有可能緩存中沒有資源再折回網絡的時間消耗。
function networkCacheRace(cacheName, request) {
var timer, TIMEOUT = 500;
/** * 網絡好的狀況下給網絡請求500ms, 若超時則從緩存中取數據 * 若網絡較差且沒有緩存, 因爲第一個 Promise 會一直處於 pending, 故此時等待網絡請求響應 */
return Promise.race([new Promise((resolve, reject) => {
// 緩存請求
timer = setTimeout(() => {
caches.open(cacheName).then( cache => {
cache.match(request).then( response => {
if (response) {
resolve(response);
}
});
});
}, TIMEOUT);
}), fetch(request).then( response => {
// 網絡請求
clearTimeout(timer);
var responseCopy = response.clone();
caches.open(cacheName).then( cache => {
cache.put(request, responseCopy);
});
return response;
}).catch(() => {
clearTimeout(timer);
return caches.open(cacheName).then( cache => {
return cache.match(request);
});
})]);
}
複製代碼
如今咱們能夠在 sw.js
中更改一下緩存策略,從而達到最理想的效果。
// sw.js
self.addEventListener('fetch', event => {
// ...
if ( /\.(js|css)$/.test(url) ) {
(cacheName = cacheMaps.cache_file)
&& e.respondWith(networkCacheRace(cacheName, request));
}
// ...
})
複製代碼
Push消息
在SW
裏,經過 push 事件
以及瀏覽器的 Push API
,能夠實現push消息
的功能。 在說道web push消息
的時候,其實涉及到兩個正在完善中的技術:消息提醒
與 信息推送
。
用SW
實現消息提醒挺簡單直接:
// app.js
// ask for permission
Notification.requestPermission(permission => {
console.log('permission:', permission);
});
// display notification
function displayNotification() {
if (Notification.permission == 'granted') {
navigator.serviceWorker.getRegistration()
.then(registration => {
registration.showNotification('this is a notification!');
});
}
}
複製代碼
// sw.js
self.addEventListener('notificationclick', event => {
// 消息提醒被點擊的事件
});
self.addEventListener('notificationclose', event => {
// 消息提醒被關閉的事件
});
複製代碼
你須要先向用戶尋求讓你的網頁產生消息提醒的權限。以後,你就能夠彈出提示信息,而後處理某些事件,好比用戶把消息關掉的事件。
信息推送涉及到利用瀏覽器提供的Push API
以及後端的配合實現。要講解如何使用Push API
徹底能夠再寫一篇文章,不過基本的套路以下:
這是個略微複雜難懂的過程,已經超出這篇文章的討論範圍。
什麼是 Workbox ?
Workbox is a library that bakes in a set of best practices and removes the boilerplate every developer writes when working with service workers.
其大概意思是它對常見的 SW
操做進行了一層封裝, 根據最佳實踐方便了開發者的使用。所以在咱們快速開發本身的 PWA
應用時使用 Workbox
是最合適不過的了。
它主要有如下幾大功能 :
直接修改sw.js
的代碼,以下:
// sw.js
// 導入谷歌提供的 Workbox 庫
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.2.0/workbox-sw.js');
if ( !workbox ) {
console.log(`Workbox didn't load.`);
return;
}
// Workbox 註冊成功, 能夠進行下一步的操做
// 當即激活, 跳過等待
workbox.skipWaiting();
workbox.clientsClaim();
// workbox.routing.registerRoute()...
複製代碼
若是瀏覽器支持,能夠直接引用API接口:
precaching
能夠在註冊成功後直接緩存的文件;routing
匹配符合規則的url
與strategies
合做來完成文件的緩存。代碼以下:
// 註冊完成後,即緩存對應的文件列表
workbox.precaching.precacheAndRoute([
'/src/static/js/index.js',
'/src/static/css/index/css'
])
// routing方法匹配請求文件路徑,strategies用來存儲對應文件
workbox.routing.registerRoute(
matchFunction, // 字符串或者是正則表達式
handler // 可使用workbox.strategies緩存策略來緩存
)
複製代碼
workbox.strategies
緩存策略有:
staleWhileRevalidate
使用已有的緩存,而後發起請求,用請求結果來更新緩存;networkFirst
先發起請求,請求成功後會緩存結果。若是失敗,則使用最新的緩存;cacheFirst
老是先使用緩存,若是無匹配的緩存,則發起網絡請求並緩存結果;networkOnly
強制發起請求;cacheOnly
強制使用緩存。官方也有給了實現邏輯以下。
只從緩存中讀取,當緩存中沒有數據時,讀取失敗。
只經過網絡請求進行資源請求,若請求失敗,則返回失敗響應。
不難看出,這種策略是爲了保證在第一次請求成功以後,後面屢次的請求始終都能返回結果。
workbox.routing.registerRoute(/\.(js|css)$/,
workbox.strategies.networkFirst({
// 給網絡請求0.5秒,若仍未返回則從緩存中取數據
networkTimetoutSeconds: 0.5,
cacheName: 'css.js',
}),
);
複製代碼
workbox.routing.registerRoute(/\.(png|jpg|jpeg|gif|webp)$/,
// 對於圖片資源使用緩存優先
workbox.strategies.cacheFirst({
cacheName: 'images',
// 設置最大緩存數量以及過時時間
plugins: [
new workbox.expiration.Plugin({
maxEntries: 60,
maxAgeSeconds: 7 * 24 * 60 * 60,
}),
],
}),
);
複製代碼
workbox.routing.registerRoute(/\.(js|css)$/,
workbox.strategies.staleWhileRevalidate({
cacheName: 'css.js',
}),
);
複製代碼
在本篇文章中詳細的記錄了有關SW
的主要功能和能給咱們帶來的好處。通常在開發中仍是推薦使用workbox
,最後若是我的有興趣的能夠本身編寫示例來驗證文章中的代碼示例。
SW主要做用
SW幾種策略
感受寫的不錯請點一下贊,據說長的帥的人都會點贊,而且點贊後走上人生巔峯。