PS: 這是本人的第一篇文章,若有不對或結構不清晰的地方,望指出,我會盡可能去完善,謝謝你們!css
什麼是workbox,workbox有什麼用途,爲何要使用它?在介紹workbox以前,咱們來先大體瞭解一下service worker,有助於咱們後面更好地去理解workbox。html
service worker是在瀏覽器後臺獨立於網頁運行的腳本,它可以實現對網絡請求進行緩存,並向網頁推送和同步信息的功能,使人更加興奮的是,它能夠實現離線的狀況下,也能看到咱們的網頁,極大提高了咱們的用戶體驗。前端
service worker 已經獲得愈來愈多的瀏覽器的支持,包括蘋果、騰訊的X5內核。蘋果從safari11開始,已經開始支持了。支持狀況以下: webpack
workbox 是 GoogleChrome 團隊推出的一套 Web App 靜態資源和請求結果的本地存儲的解決方案,該解決方案包含一些 Js 庫和構建工具,在 Chrome Submit 2017 上首次隆重面世。而在 workbox 背後則是 Service Worker 和 Cache API 等技術和標準在驅動。在 Workebox 以前,GoogleChrome 團隊較早時間推出過 sw-precache 和 sw-toolbox 庫,可是在 GoogleChrome 工程師們看來,workbox 纔是真正能方便統一的處理離線能力的更完美的方案,因此中止了對 sw-precache 和 sw-toolbox 的維護。那workbox能解決什麼問題呢?web
在service worker中,若是咱們要攔截並代理全部的請求,須要咱們手動去維護一套緩存列表。可是如今前端開發,多數用webpack、gulp、grant來構建前端的代碼,致使咱們的文件名可能會常常發生,這個時候,特別是中大型的多頁應用,緩存列表的內容可能會很是多,手動維護就顯得很是麻煩,維護成本也變得很高。gulp
這個時候,workbox的橫空出世,就是爲了解決上面的問題。api
下面來看下workbox的例子。數組
index.html瀏覽器
<script>
// Register A service worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register(`./build.sw.js`)
.then(function(registration) {
// Registration was successful
console.log('[success] register ')
}, function(err) {
// registration failed :(
console.log('[fail]: ', err);
});
});
<script>
}
複製代碼
// 首先引入 Workbox 框架
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.3.0/workbox-sw.js');
// 註冊成功後要當即緩存的資源列表
workbox.precaching.precacheAndRoute([
{
"url": "css/index.css",
"revision": "835ba5c3"
},
{
"url": "images/xxx.png",
"revision": "b1537bfs"
},
{
"url": "index.html",
"revision": "b331f695"
},
{
"url": "js/index.js",
"revision": "4d562866"
}
]);
// 緩存策略
workbox.routing.registerRoute(
new RegExp(''.*\.html'), workbox.strategies.networkFirst() ); workbox.routing.registerRoute( new RegExp('.*\.(?:js|css)'), workbox.strategies.cacheFirst() ); workbox.routing.registerRoute( new RegExp('https://your\.cdn\.com/'), workbox.strategies.staleWhileRevalidate() ); workbox.routing.registerRoute( new RegExp('https://your\.img\.cdn\.com/'), workbox.strategies.cacheFirst({ cacheName: 'example:img' }) ); 複製代碼
實現的效果以下: 緩存
咱們來看下build.sw.js文件的內容,主要包含緩存列表和緩存策略。這裏面的內容不用咱們手動生成,workbox有三種方式生成,咱們可使用workbox-webpack-plugin、workbox-cli、workbox-build。咱們暫不討論具體的實現,在這裏,咱們先來了解一下預緩存列表和緩存策略這兩個東西。
若是咱們要緩存靜態資源,平時不會常常更新,只有到發版時纔會修改了資源的hash值,才須要從新更新的,那那 precache 預緩存應該是你所期待的。
workbox 提供了一種很是方便的 API 幫助咱們解決 precache 的問題,咱們可使用workbox.precaching來配置,配置格式以下:
workbox.precaching.precacheAndRoute([
{
"url": "將要預緩存的文件 URL",
"revision": "緩存的hash值"
},
])
複製代碼
路由請求緩存是指經過對匹配路由給文件採起不用的緩存方式,這個能夠經過workbox.routing.registerRoute來進行配置。 路由匹配的方式有三種:
// 能夠直接是當前項目下的絕對路徑
workbox.routing.registerRoute(
'path/to/logo.png',
handler // handler 是作緩存策略的回調函數,一般指後面所會降到的 '緩存策略函數'
);
// 也能夠是完整的帶完整 host 的 URL 路徑,這裏的 URL 必須是 https 的
workbox.routing.registerRoute(
'https://example.com/a/b/c.jpg',
handler
);
複製代碼
workbox.routing.registerRoute(
new RegExp('.*\.(js|css|jpg|png|gif)'), // 這裏是任何正則都行,只要能匹配得上的請求路由地址
handler
);
複製代碼
3.經過回調函數的方式進行匹配
// 經過回調函數來匹配請求路由將會讓策略更加靈活
const customFun = ({url, event}) => {
// 若是請求路由匹配了就返回true,也能夠返回一個參數對象以供 handler 接收處理
return false;
};
workbox.routing.registerRoute(
customFun,
handler
);
複製代碼
緩存策略是指對於匹配到的路由,採起何種方式進行緩存。 workbox提供了兩種配置緩存策略的方式
如下介紹workbox默認提供的幾種緩存策略,包含有五種,分別是:
這種策略的意思是當請求的路由有對應的 Cache 緩存結果就直接返回,在返回 Cache 緩存結果的同時會在後臺發起網絡請求拿到請求結果並更新 Cache 緩存,若是原本就沒有 Cache 緩存的話,直接就發起網絡請求並返回結果。 使用方式以下:
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.staleWhileRevalidate()
);
複製代碼
這種策略就是當請求路由是被匹配的,就採用網絡優先的策略,也就是優先嚐試拿到網絡請求的返回結果,若是拿到網絡請求的結果,就將結果返回給客戶端而且寫入 Cache 緩存,若是網絡請求失敗,那最後被緩存的 Cache 緩存結果就會被返回到客戶端 使用方式以下:
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.networkFirst()
);
複製代碼
這個策略的意思就是當匹配到請求以後直接從 Cache 緩存中取得結果,若是 Cache 緩存中沒有結果,那就會發起網絡請求,拿到網絡請求結果並將結果更新至 Cache 緩存,並將結果返回給客戶端。
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.cacheFirst()
);
複製代碼
比較直接的策略,直接強制使用正常的網絡請求,並將結果返回給客戶端,這種策略比較適合對實時性要求很是高的請求。
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.networkOnly()
);
複製代碼
這個策略也比較直接,直接使用 Cache 緩存的結果,並將結果返回給客戶端,這種策略比較適合一上線就不會變的靜態資源請求。
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.cacheOnly()
);
複製代碼
在咱們的項目中,咱們以DomContentLoaded的時間做爲參考點,對比有加service worker 和未加的service worker狀況。
測試條件
以首頁爲例,在不一樣的網絡環境下,發起10次網絡請求,而後取平均值,做爲它們的最終結果,測試結果以下:
在使用workbox的過程當中,會遇到一些問題,下面列出幾點,也算是作個總結:
1. service worker 註冊文件放置的位置
在頁面註冊service worker的時候,儘可能註冊到項目的根目錄下,這樣才能最大的發揮service worker的做用
// build.sw.js最好放在項目的根目錄下,才能發揮最大的緩存效果
navigator.serviceWorker.register(`./build.sw.js`)
// 若是這樣配置的話,就只有path目錄下面的文件才能實現緩存,其餘目錄,包括根目錄的都不能緩存
navigator.serviceWorker.register(`./path/build.sw.js`)
複製代碼
2.使用workbox 命令行生成預緩存列表的注意點
咱們先預設一下應用場景:假設你的項目在目錄 /app 下,必須保證在你的項目根目錄下有一個 app/sw.js 包含如下內容:
// 一般項目中的 sw.js 源文件都是經過這樣預留一個空數組的方式來預緩存內容列表的
workbox.precaching.precacheAndRoute([]);
複製代碼
這樣才能保證能將生成的預緩存內容列表內容注入到 Service Worker 文件中。
3.緩存策略設置
在通過一段時間的使用和思考之後,給出認爲較爲合理的緩存策略:
HTML,若是想讓頁面離線能夠訪問,使用 NetworkFirst,若是不須要離線訪問,使用 NetworkOnly,其餘策略均不建議對 HTML 使用。
CSS 和 JS,狀況比較複雜,由於通常站點的 CSS,JS 都在 CDN 上,SW 並無辦法判斷從 CDN 上請求下來的資源是否正確(HTTP 200),若是緩存了失敗的結果,問題就大了。建議使用 Stale-While-Revalidate 策略,既保證了頁面速度,即使失敗,用戶刷新一下就更新了。
若是CSS,JS 與站點在同一個域下,而且文件名中帶了 Hash 版本號,那能夠直接使用 Cache First 策略。
圖片建議使用 Cache First,並設置必定的失效事件,請求一次就不會再變更了。
若是你們在使用過程當中有更友好的策略,麻煩也貢獻大家的策略,你們共同窗習,共同進步。
還有,要牢記,對於不在同一域下的任何資源,絕對不能使用 Cache only 和 Cache first。
4.service worker的運行環境
須要注意的是,Service Worker 腳本除了域名爲 localhost 時能運行在 http 協議下之外,只能運行 https 協議下。
5. 使用Service Worker緩存請求時,POST請求沒法緩存
Google對web的標準化仍是遵循的,SW認爲POST請求就是象服務器提交資源,不存在緩存需求
參考文檔: