借用workbox實現離線緩存應用

PS: 這是本人的第一篇文章,若有不對或結構不清晰的地方,望指出,我會盡可能去完善,謝謝你們!css

什麼是workbox,workbox有什麼用途,爲何要使用它?在介紹workbox以前,咱們來先大體瞭解一下service worker,有助於咱們後面更好地去理解workbox。html

一. service worker

service worker是在瀏覽器後臺獨立於網頁運行的腳本,它可以實現對網絡請求進行緩存,並向網頁推送和同步信息的功能,使人更加興奮的是,它能夠實現離線的狀況下,也能看到咱們的網頁,極大提高了咱們的用戶體驗。前端

service worker 已經獲得愈來愈多的瀏覽器的支持,包括蘋果、騰訊的X5內核。蘋果從safari11開始,已經開始支持了。支持狀況以下: webpack

image

二. 爲何要用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能解決什麼問題呢?web

在service worker中,若是咱們要攔截並代理全部的請求,須要咱們手動去維護一套緩存列表。可是如今前端開發,多數用webpack、gulp、grant來構建前端的代碼,致使咱們的文件名可能會常常發生,這個時候,特別是中大型的多頁應用,緩存列表的內容可能會很是多,手動維護就顯得很是麻煩,維護成本也變得很高。gulp

這個時候,workbox的橫空出世,就是爲了解決上面的問題。api

workbox的一些特性:

  • 無論你的站點是哪一種方式構建的,均可以實現離線緩存的效果;
  • 自動管理好緩存列表,包括更新、同步、刪除舊的緩存等;
  • 配置簡單卻不失靈活,能夠徹底自定義相關需求(支持 Service Worker 相關的特性如 Web Push, Background sync 等)。
  • 針對各類應用場景的多種緩存策略。

三. workbox的使用

下面來看下workbox的例子。數組

  1. 在入口頁面的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>
}
複製代碼
  1. 在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' }) ); 複製代碼

實現的效果以下: 緩存

image

咱們來看下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來進行配置。 路由匹配的方式有三種:

  1. 經過字符串的方式進行匹配
// 能夠直接是當前項目下的絕對路徑
workbox.routing.registerRoute(
    'path/to/logo.png',
    handler // handler 是作緩存策略的回調函數,一般指後面所會降到的 '緩存策略函數'
);

// 也能夠是完整的帶完整 host 的 URL 路徑,這裏的 URL 必須是 https 的
workbox.routing.registerRoute(
    'https://example.com/a/b/c.jpg',
    handler
);
複製代碼
  1. 經過正則的方式進行匹配
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次網絡請求,而後取平均值,做爲它們的最終結果,測試結果以下:

image
經過上面的數據能夠得出幾個結論:

  • 在弱環境下,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請求就是象服務器提交資源,不存在緩存需求


參考文檔:

developers.google.com/web/tools/w…

zoumiaojiang.com/article/ama…

相關文章
相關標籤/搜索