PS: 這是本人的第一篇文章,如有不對或結構不清晰的地方,望指出,我會盡量去完善,謝謝大家!
什麼是workbox,workbox有什麼用途,為什麼要使用它?在介紹workbox之前,我們來先大致瞭解一下service worker,有助於我們後面更好地去理解workbox。
一. service worker
service worker是在瀏覽器後臺獨立於網頁執行的指令碼,它能夠實現對網路請求進行快取,並向網頁推送和同步資訊的功能,令人更加興奮的是,它可以實現離線的情況下,也能看到我們的網頁,極大提升了我們的使用者體驗。
service worker 已經得到越來越多的瀏覽器的支援,包括蘋果、騰訊的X5核心。蘋果從safari11開始,已經開始支援了。支援情況如下:
二. 為什麼要用workbox
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能解決什麼問題呢?
在service worker中,如果我們要攔截並代理所有的請求,需要我們手動去維護一套快取列表。但是現在前端開發,多數用webpack、gulp、grant來構建前端的程式碼,導致我們的檔名可能會經常發生,這個時候,特別是中大型的多頁應用,快取列表的內容可能會非常多,手動維護就顯得非常麻煩,維護成本也變得很高。
這個時候,workbox的橫空出世,就是為了解決上面的問題。
workbox的一些特性:
- 不管你的站點是哪種方式構建的,都可以實現離線快取的效果;
- 自動管理好快取列表,包括更新、同步、刪除舊的快取等;
- 配置簡單卻不失靈活,可以完全自定義相關需求(支援 Service Worker 相關的特性如 Web Push, Background sync 等)。
- 針對各種應用場景的多種快取策略。
三. workbox的使用
下面來看下workbox的例子。
- 在入口頁面的onload中,註冊一個service worker,註冊時引入快取列表檔案,也就是build.sw.js。
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>
}
複製程式碼
- 在build.sw.js頁面配置快取列表和快取策略
// 首先引入 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.strategies API 提供的 快取策略。
- 提供一個自定義返回帶有返回結果的 Promise 的回撥方法。
以下介紹workbox預設提供的幾種快取策略,包含有五種,分別是:
- Stale While Revalidate
- Network First
- Cache First
- Network Only
- Cache Only
Stale While Revalidate
這種策略的意思是當請求的路由有對應的 Cache 快取結果就直接返回,在返回 Cache 快取結果的同時會在後臺發起網路請求拿到請求結果並更新 Cache 快取,如果本來就沒有 Cache 快取的話,直接就發起網路請求並返回結果。 使用方式如下:
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.staleWhileRevalidate()
);
複製程式碼
Network First
這種策略就是當請求路由是被匹配的,就採用網路優先的策略,也就是優先嚐試拿到網路請求的返回結果,如果拿到網路請求的結果,就將結果返回給客戶端並且寫入 Cache 快取,如果網路請求失敗,那最後被快取的 Cache 快取結果就會被返回到客戶端 使用方式如下:
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.networkFirst()
);
複製程式碼
Cache First
這個策略的意思就是當匹配到請求之後直接從 Cache 快取中取得結果,如果 Cache 快取中沒有結果,那就會發起網路請求,拿到網路請求結果並將結果更新至 Cache 快取,並將結果返回給客戶端。
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.cacheFirst()
);
複製程式碼
Network Only
比較直接的策略,直接強制使用正常的網路請求,並將結果返回給客戶端,這種策略比較適合對實時性要求非常高的請求。
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.networkOnly()
);
複製程式碼
Cache Only
這個策略也比較直接,直接使用 Cache 快取的結果,並將結果返回給客戶端,這種策略比較適合一上線就不會變的靜態資源請求。
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.cacheOnly()
);
複製程式碼
四. 使用workerbox後的效果
在我們的專案中,我們以DomContentLoaded的時間作為參考點,對比有加service worker 和未加的service worker情況。
測試條件
以首頁為例,在不同的網路環境下,發起10次網路請求,然後取平均值,作為它們的最終結果,測試結果如下:
通過上面的資料可以得出幾個結論:- 在弱環境下,service worker的優勢越發明顯,
- 即使在wifi環境下面,由於存在快取的情況,瀏覽器載入的速度也比未使用service worker的時間要短。
- 在無網路環境的情況,也可以做到離線快取的效果,極大地提升頁面的使用者體驗。
五. 幾個注意點
在使用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請求就是象伺服器提交資源,不存在快取需求
參考文件: