神奇的 Workbox 3.0 讓你的 Web 站點輕鬆做到離線可訪問

神奇的 Workbox 3.0

讓你的 Web 站點輕鬆做到離線可訪問

Posted by zoumiaojiang on 2018-01-16

Peace

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 的維護。

從 sw-precache 和 sw-toolbox 的相關 issue 來看,衆多開發者對它們也是頗有怨言。但是 workbox 發佈了 2.x 版本後其實反響一般,總的來說我認爲還是 API 太混亂了,雖然功能很強大但是設計條理不清晰,其學習成本導致很難推廣開,但是從目前 workbox3 alpha 版本的文檔和 API 來看,我個人認爲 workbox3 終於成爲了能夠很方便簡單的解決 Service Worker 絕大多數問題的神器,所以寫這篇文章來全面介紹一下。

注意,當前 workbox 3 還是 alpha 發佈階段,API 和接口還有可能調整,文中所提到的一些工具可能 Google 方面還沒有完全 ready, 本文爲正式發佈前的嚐鮮版,後續正式發佈後本會隨版更新。

概述

workbox 現在已經發布到了 3.0 alpha 版本,我們可以先了解一下 workbox:

  • 不管你的站點是何種方式構建的,都可以爲你的站點提供離線訪問能力。
  • 就算你不考慮離線能力,也能讓你的站點訪問速度更加快。
  • 幾乎不用考慮太多的具體實現,只用做一些配置。
  • 簡單卻不失靈活,可以完全自定義相關需求(支持 Service Worker 相關的特性如 Web Push, Background sync 等)。
  • 針對各種應用場景的多種緩存策略。

bingo! 如果你被這些特性吸引住了,可以往下看看,我們在這裏講的是 workbox 3.0 的內容,當然 workbox 在解決的核心問題方面和之前的版本沒有太大的出入,只是 API 進行了一些調整,並且在構建相關方面考慮的更加完善,workbox 進入 3.0 以後,API 看起來清晰多了,接下來一起看看 workbox 3 具體內容吧。

用法

想要使用 workbox,首先需要在你的項目中創建一個 Service Worker 文件 sw.js 並且在你的站點上註冊:

1
2
3
4
5
6
7
8
9
<script>
// 可以這麼註冊 Service Worker
if ('serviceWorker' in navigator) {
    // 爲了保證首屏渲染性能,可以在頁面 load 完之後註冊 Service Worker
    window.onload = function () {
        navigator.serviceWorker.register('/sw.js');
    };
}
</script>

有了 sw.js 之後就可以使用 workbox 了,你只需要在 sw.js 中導入 workbox 就可以使用了:

1
2
3
4
5
6
7
8
9
10
11
// workbox 2.x 是將 workbox 核心內容放在 workbox-sw node_modules 包裏維護的
// workbox 3.x 開始是將 workbox 核心 lib 放在 CDN 維護
// 當然也可以挪到自己的 CDN 維護
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.0.0-alpha.3/workbox-sw.js');

if (workbox) {
    console.log(`Yay! workbox is loaded 🎉`);
}
else {
    console.log(`Boo! workbox didn't load 😬`);
}

當在 sw.js 能夠拿到 workbox 全局變量,表明 workbox 可以使用了,workbox 能幹什麼呢?

  • 通過 workbox.precaching 模塊來處理 Service Worker 靜態資源的預緩存。
  • 通過 workbox.routing 模塊提供的路由控制和 workbox.strategies 模快提供的緩存策略控制幫助你做動態緩存。
  • 使用 workbox 插件做一些 Service Worker 相對獨立的工作,比如 更新提醒, Background Sync 等

precache (預緩存) 靜態文件

如果你有一些靜態資源是需要永遠的離線緩存,除非重新上線才更新緩存的話,那 precache 預緩存應該是你所期待的,如果瞭解 Service Worker 的生命週期的話,precache 的工作是在 Service Worker install 時候通過 Cache API 完成的,具體可以瞭解 Service Work 生命週期

workbox 提供了一種非常方便的 API 幫助我們解決 precache 的問題,和之前 Google 的 precache 方案 sw-precache 的工作類似。workbox 通過使用 precaching.precacheAndRoute 接口完成 precache 操作:

1
2
3
4
5
6
7
8
workbox.precaching.preacheAndRoute([
    '/styles/index.0c9a31.css',
    '/scripts/main.0d5770.js',
    {
        url: '/index.html',
        revision: '383676'
    },
]);

以上這段代碼會在 Service Worker 安裝成功的時候下載 /styles/index.0c9a31.css, /scripts/main.0d5770.js/index.html 文件,並且會以構造路由的方式將這些文件都存儲到 Cache Storage 中。這個傳入的數組其實就是預緩存內容列表,有兩種形式,一種是直接寫文件名的字符串(帶 Hash 的),一種是如下所示帶有 url 和 revesion 值的對象:

1
2
3
4
{
    url: '將要預緩存的文件 URL,
    revision: '文件內容的 Hash 值'
}

這裏需要注意的是這個 revision 的值,當預緩存的文件就任何變動的時候就會被更新,如果 revision 沒有更新,那當你更新 Service Worker 的時候,被緩存的文件也不會被更新。

我們接下來介紹一些幫助我們生成預緩存內容列表的 precaching.precacheAndRoute API 的配置選項。

處理 / 和 /index.html

通常當用戶訪問 / 時,對應的訪問的頁面 HTML 文件是 /index.html,默認情況下,precache 路由機制會在任何 URL 的結尾的 / 後加上 index.html,這就以爲着你預緩存的任何 index.html 都可以通過 /index.html 或者 / 訪問到。

當然,你也可以通過 directoryIndex 參數禁用掉這個默認行爲:

1
2
3
4
5
6
7
8
9
10
11
12
13
workbox.precaching.preacheAndRoute(
    [
        '/styles/index.0c9a31.css',
        '/scripts/main.0d5770.js',
        {
            url: '/index.html',
            revision: '383676'
        },
    ],
    {
        directoryIndex: null
    }
);

忽略請求參數

例如,如果我麼想要讓 key1 參數不是 /example.html?key1=1&key2=2 的一部分,你只需要設置 ignoreURLParametersMatching 參數把它排除掉:

1
2
3
4
5
6
7
8
9
10
11
workbox.precaching.precacheAndRoute(
    [
        {
            url: '/example.html?key2=2',
            revision: '6821e0'
        }
    ],
    {
        ignoreUrlParametersMatching:[/key1/],
    }
);

這樣 /example.html?key1=1&key2=2 這個路由對應的內容就可以被預緩存了。

生成預緩存列表

靠手動維護 precache.precacheAndRoute API 中的預緩存內容列表是不可能的,revision 我們無法手動維護,所以我們肯定是要藉助一些工具來幹這個事情,好在 workbox 提供了多種方式讓我們選擇:

  • workbox 命令行
  • workbox-build npm 包
  • workbox-webpack-plugin

在使用以上三種方式生成預緩存內容列表之前,我們先預設一下應用場景:假設你的項目在目錄 /app 下,必須保證在你的項目根目錄下有一個 app/sw.js 包含以下內容:

1
2
// 通常項目中的 sw.js 源文件都是通過這樣預留一個空數組的方式來預緩存內容列表的
workbox.precaching.precacheAndRoute([]);

這樣才能保證能將生成的預緩存內容列表內容注入到 Service Worker 文件中。

workbox 命令行

workbox 提供了一套命令行,專門來幫助我們注入預緩存內容列表,可以幫助我們生成注入預緩存內容列表所需要的 workbox-cli-config.js 配置文件,然後通過命令行使用配置文件就可以生成一個預緩存列表的代碼並注入到之前自定義的 app/sw.js 文件中,最終編譯成線上所需要的 Service Worker 文件:dist/sw.js

安裝命令行:

1
npm install -g workbox-cli

所以我們先要有一個 workbox-cli-config.js 文件,然後根據這個文件結合 /app/sw.js 源文件來生成一個含有預緩存列表的 dist/sw.js 文件。接下來我們可以看下具體這一系列流程需要怎麼操作。

首先執行 workbox generate:sw 命令生成一個配置文件 workbox-cli-config.js,這個命令執行之後將會彈出一些問題讓你選擇,如下所示:

1
2
3
4
5
>$ workbox generate:sw
? What is the root of your web app? app
? Which file types would you like to cache? (Press <space> to select, <a> to toggle all, <i> to inverse selection)html,ico,svg,png,js,css
? What should the path of your new service worker file be (i.e. './build/sw.js')? dist/sw.js
? Last Question - Would you like to save these settings to a config file? Yes

回答完這些問題之後你就可以在你的項目裏導出一個新的 workbox-cli-config.js 配置文件了大概長以下這個樣子:

1
2
3
4
5
6
7
8
9
// workbox-config.js
module.exports = {
    "globDirectory": "app/",
    "globPatterns": [
        "**/*.{html,ico,svg,png,js,css}"
    ],
    "swDest": "dist/sw.js",
    "swSrc": "app/sw.js"
};

拿到了 workbox-cli-config.js 配置文件之後可以執行 workbox inject:manifest workbox-cli-config.js 命令生成編譯後的 dist/sw.js 文件了,這一步乾的事情就是把預緩存內容列表注入到 app/sw.js 中,一般只有在上線前才用命令行注入預緩存內容列表,通常我們都不會手動去執行這些命令的,比較合理的做法是實現生成好 workbox-cli-config.js 文件,然後在構建腳本中配置上自動執行 workbox inject:manifest 命令。

workbox inject:manifest 命令的做法就是去匹配 /app/sw.js 中的 workbox.precaching.precacheAndRoute([]) 方法的正則,然後通過 replace 內容注入的,可以參考下面的 workbox-build 的介紹。

workbox-build

使用命令行總感覺太傻了,操作步驟也比較繁瑣,爲了使得預緩存工作更加簡便靈活,workbox 也提供了一個 NPM 包 – workbox-build,你可以在任何構建工具中都使用。

可以在你的工程根目錄中執行以下命令安裝 workbox-build 包:

1
npm install --save-dev workbox-build

然後直接可以在你想要處理 Service Worker 預緩存的地方引入 workbox-build 庫,並且調用其 injectManifest 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const workboxBuild = require('workbox-build');

workboxBuild.injectManifest({
    swSrc: path.join(__dirname, 'app', 'sw.js'),
    swDest: path.join(__dirname, 'dist', 'sw.js'),
    globDirectory: './dist/',
    globPatterns: ['**\/*.{html,js,css}'],
    globIgnores: ['admin.html'],
    templatedUrls: {
        '/shell': ['dev/templates/app-shell.html', 'dev/**\/*.css'],
    },

    // 要替換的預留代碼區正則
    injectionPointRegexp: /(\.precacheAndRoute\()\s*\[\s*\]\s*(\))/,
})
.catch(err => {
    console.error(`Unable to inject the precache manifest into sw.js`);
    throw err;
});

在構建文件中執行這段代碼就會讀取 app/sw.js 文件然後生成一個 dist/sw.js 文件含有注入的預緩存內容列表。

關於如何使用 injectManifest 方法可以查看 workbox-build 的 injectManifest 方法 全部參數

workbox-webpack-plugin

有了 workbox-build 可想而知就能搞出很多實用的預緩存插件方案,比如 Webpack、Gulp 等插件,workbox 官方也提供了一個插件 workbox-webpack-plugin,只需要通過以下方式,就可以將插件安裝到你的 webpack 項目中:

1
npm install --save-dev workbox-webpack-plugin

然後就只需要將插件添加到 Webpack 配置中就可以正常使用了,插件參數和 workbox-build 的 injectManifest 方法保持一致:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const workboxPlugin = require('workbox-webpack-plugin');

// ...
webpack({
    plugins: [
        // ...
        new workboxPlugin({
            swSrc: './src/sw.js',
            swDest: './dist/sw.js',
            globDirectory: './dist/',
            globPatterns: ['**/*.{html,js,css}'],
        })
    ]
    // ...
});

這裏有個地方和之前提到的替換預留的 app/sw.js 不一樣:使用 workbox 提供的 Webpack 插件必須在 app/sw.js 中包含以下代碼才能完成預緩存內容列表注入工作

1
workbox.precaching.precacheAndRoute(self.__precacheManifest || []);

當插件跑起來之後,會在 /dist/sw.js 中增加一段 importScripts() 代碼用來引入一個模塊,這個模塊的內容就是 self.__precacheManifest,也就是預緩存內容列表的內容,具體的效果可以在項目中使用 workbox-webpack-plugin 嘗試一下,看看 build 後的 dist/sw.js 結果就會比較清楚了。

路由請求緩存

路由請求緩存是通過文件路由匹配的模式分別對制定的路由文件做不同策略緩存的方式,這部分工作可以在 app/sw.js 中直接使用 workbox 提供的 workbox.routing.registerRoute API 完成,這個 API 可以理解爲幹了兩件事情,一、通過請求路由配置匹配到指定待緩存文件或請求內容二、通過第二個參數的處理回調函數決定用何種策略來緩存匹配上的文件。有 三種 方法可以通過 workbox-route 來匹配一個請求 URL

  • 字符串方式
1
2
3
4
5
6
7
8
9
10
11
// 可以直接是當前項目下的絕對路徑
workbox.routing.registerRoute(
    '/logo.png',
    handler // handler 是做緩存策略的回調函數,通常指後面所會降到的 '緩存策略函數'
);

// 也可以是完整的帶完整 host 的 URL 路徑,這裏的 URL 必須是 https 的
workbox.routing.registerRoute(
    'https://some-host/some-path/logo.png',
    handler
);
  • 正則表達式方式
1
2
3
4
workbox.routing.registerRoute(
    new RegExp('.*\.js'), // 這裏是任何正則都行,只要能匹配得上的請求路由地址
    handler
);
  • 回調函數方式
1
2
3
4
5
6
7
8
9
10
// 通過回調函數來匹配請求路由將會讓策略更加靈活
const matchFunction = ({url, event}) => {
    // 如果請求路由匹配了就返回 true,也可以返回一個參數對象以供 handler 接收處理
    return false;
};

workbox.routing.registerRoute(
    matchFunction,
    handler
);

上面講到了匹配請求路由的三種方式,接下來可以講講如何處理匹配上的請求所返回的內容,也就是上面三種路由匹配方式的 handler,通常有兩種做法:

  • 使用一種 workbox 通過 workbox.strategies API 提供的 緩存策略
  • 提供一個自定義返回帶有返回結果的 Promise 的回調方法。

路由請求緩存策略

下面介紹一下 workbox 默認提供的幾種緩存策略 API,這些 API 可以被當成 handler 使用。

Stale While Revalidate

這種策略的意思是當請求的路由有對應的 Cache 緩存結果就直接返回,在返回 Cache 緩存結果的同時會在後臺發起網絡請求拿到請求結果並更新 Cache 緩存,如果本來就沒有 Cache 緩存的話,直接就發起網絡請求並返回結果,這對用戶來說是一種非常安全的策略,能保證用戶最快速的拿到請求的結果,但是也有一定的缺點,就是還是會有網絡請求佔用了用戶的網絡帶寬。可以像如下的方式使用 State While Revalidate 策略:

1
2
3
4
workbox.routing.registerRoute(
    match, // 匹配的路由
    workbox.strategies.staleWhileRevalidate()
);

Network First

這種策略就是當請求路由是被匹配的,就採用網絡優先的策略,也就是優先嚐試拿到網絡請求的返回結果,如果拿到網絡請求的結果,就將結果返回給客戶端並且寫入 Cache 緩存,如果網絡請求失敗,那最後被緩存的 Cache 緩存結果就會被返回到客戶端,這種策略一般適用於返回結果不太固定或對實時性有要求的請求,爲網絡請求失敗進行兜底。可以像如下方式使用 Network First 策略:

1
2
3
4
workbox.routing.registerRoute(
    match, // 匹配的路由
    workbox.strategies.networkFirst()
);

Cache First

這個策略的意思就是當匹配到請求之後直接從 Cache 緩存中取得結果,如果 Cache 緩存中沒有結果,那就會發起網絡請求,拿到網絡請求結果並將結果更新至 Cache 緩存,並將結果返回給客戶端。這種策略比較適合結果不怎麼變動且對實時性要求不高的請求。可以像如下方式使用 Cache First 策略:

1
2
3
4
workbox.routing.registerRoute(
    match, // 匹配的路由
    workbox.strategies.cacheFirst()
);

Network Only

比較直接的策略,直接強制使用正常的網絡請求,並將結果返回給客戶端,這種策略比較適合對實時性要求非常高的請求。可以像如下方式使用 Network Only 策略:

1
2
3
4
workbox.routing.registerRoute(
    match, // 匹配的路由
    workbox.strategies.networkOnly()
);

Cache Only

這個策略也比較直接,直接使用 Cache 緩存的結果,並將結果返回給客戶端,這種策略比較適合一上線就不會變的靜態資源請求。可以像如下方式使用 Cache Only 策略:

1
2
3
4
workbox.routing.registerRoute(
    match, // 匹配的路由
    workbox.strategies.networkOnly()
);

無論使用何種策略,你都可以通過自定義一個緩存來使用或添加插件(後面我們會介紹 workbox 插件)來定製路由的行爲(以何種方式返回結果)。

1
2
3
4
5
6
7
8
9
workbox.strategies.staleWhileRevalidate({
    // 使用用戶自定義的緩存名稱
    cacheName: 'my-cache-name',

    // 使用 workbox 插件
    plugins: [
        // ...
    ]
});

當然,這些配置通常需要在緩存請求時更安全,也就是說,需要限制緩存的時間或者確保設備上用的數據是被限制的。

自定義策略

如果以上的那些策略都不太能滿足你的請求的緩存需求,那就得想想辦法自己定製一個合適的策略,甚至是不同情況下返回不同的請求結果,workbox 也考慮到了這種場景(這也是爲什麼我會極力推薦 workbox 的原因),當然,最簡單的方法是直接在 Service Worker 文件裏通過最原始的 fetch 事件控制緩存策略。也可以使用 workbox 提供的另一種方式:傳入一個帶有對象參數的回調函數,對象中包含匹配的 url 以及請求的 fetchEvent 參數,回調函數返回的就是一個 response 對象,具體用法如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
workbox.routing.registerRoute(
    ({url, event}) => {
        return {
            name: 'workbox',
            type: 'guide',
        };
    },
    ({url, event, params}) => {
        // 返回的結果是:A guide on workbox
        return new Response(
            `A ${params.type} on ${params.name}`
        );
    }
);

第三方請求的緩存

如果有些請求的域和當前 Web 站點不一致,那可以被認爲是第三方資源或請求,針對第三方請求的緩存,因爲 Workbox 無法獲取第三方路由請求的狀態,當請求失敗的情況下 workbox 也只能選擇緩存錯誤的結果,所以 workbox 3 原則上默認不會緩存第三方請求的返回結果。也就是說,默認情況下如下的緩存策略是不會生效的:

1
2
3
4
workbox.routing.registerRoute(
    'https://notzoumiaojiang.com/example-script.min.js',
    workbox.strategies.cacheFirst(),
);

當然,並不是所有的策略在第三方請求上都不能使用,workbox 3 可以允許 networkFirst 和 stalteWhileRevalidate 緩存策略生效,因爲這些策略會有規律的更新緩存的返回內容,畢竟每次請求後都會更新緩存內容,要比直接緩存安全的多。

如果你強制使用 workbox 3 不推薦的緩存策略去緩存第三方請求,那 workbox 在 DevTools 裏的 console 中會報警報哦。

如果你還是執意要緩存第三方請求的結果的話,workbox 3 也考慮到了確實會有這種難以扼殺的需求,提供了一個非常人性化的方式滿足需求:利用 workbox.cacheableResponse.Plugin 來指定只緩存請求成功的結果,這樣就打消掉我們之前對於不安全結果被緩存的顧慮了。鵝妹子英!(後面我們會介紹插件機制)

1
2
3
4
5
6
7
8
9
10
11
workbox.routing.registerRoute(
    'https://notzoumiaojiang.com/example-script.min.js',
    workbox.strategies.cacheFirst({
        plugins: [
            // 這個插件是讓匹配的請求的符合開發者指定的條件的返回結果可以被緩存
            new workbox.cacheableResponse.Plugin({
                statuses: [0, 200]
            })
        ]
    }),
);

workbox 配置

workbox 3 提供了一些配置項,都封裝在 workbox.core API 中,可以稍微瞭解一下。

配置緩存名稱

通過 DevTools -> Applications -> Caches 可以發現,workbox 對於緩存命名有一些規則的:

1
<prefix>-<ID>-<suffix>

每個命名都有個前綴和後綴,中間纔是真正的命名 ID,主要是爲了更大限度的防止重名的情況發生,可以通過以下這種方式分別對 precache 和 runtime 形式的緩存進行自定義命名:

1
2
3
4
5
6
workbox.core.setCacheNameDetails({
    prefix: 'my-app',
    suffix: 'v1',
    precache: 'custom-precache-name',// 不設置的話默認值爲 'precache'
    runtime: 'custom-runtime-name' // 不設置的話默認值爲 'runtime'
});

通過以上設置後,precache 類型的緩存名稱爲 my-app-custom-precache-name-<ID>-v1,runtime 類型的緩存名稱爲 my-app-custom-runtime-name-<ID>-v1。workbox 推薦儘量爲你的每個項目設置不同的 prefix,這樣你在本地 locahost 調試 Service Worker 的時候可以避免衝突。而 suffix 可以用來控制緩存版本,讓站點的 Service Worker 更新機制變得清晰維護。

workbox 爲了讓 Web App 的緩存管理的更加細粒度的清晰可維護,也提供了策略級別的緩存命名設置,可以通過策略 API 的 cacheName 參數進行設置:

1
2
3
4
5
6
workbox.routing.registerRoute(
    /.*\.(?:png|jpg|jpeg|svg|gif)/g,
    new workbox.strategies.CacheFirst({
        cacheName: 'my-image-cache',
    })
);

這樣,對應的圖片相關的 cacheFirst 策略的緩存都會以 my-image-cache-<ID> 的形式命名,這裏要注意的是:prefix 和 suffix 是不需要設置的

指定 development 環境

workbox 開發過程中是需要 debug 的,所以 workbox 3 也提供了 logger 機制幫助我們排查問題,但是在生產環境下,我們不希望也產生 logger 信息,所以 workbox 提供了「指定當前環境」 的設置:

1
2
3
4
5
// 設置爲開發模式
workbox.setConfig({debug: true});

// 設置爲線上生產模式
workbox.setConfig({debug: false});

配置日誌 Level

workbox 3 提供 logger 機制幫助我們更好的調試 workbox,一共有四種 log level:debuglogwarnerror

workbox-core-logs.pnguploading.4e448015.gif轉存失敗重新上傳取消workbox-core-logs.pnguploading.4e448015.gif正在上傳…重新上傳取消workbox-core-logs.pnguploading.4e448015.gif正在上傳…重新上傳取消workbox-core-logs.pnguploading.4e448015.gif正在上傳…重新上傳取消workbox-core-logs.pnguploading.4e448015.gif正在上傳…重新上傳取消workbox logs

可以通過以下方式設置 log 的 level,這樣就可以只看到某個 level 的 log 信息,讓調試的過程中更加專注。具體的設置方式是通過 worbox.core API 中的 setLogLevel 方法來完成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 展示所有的 log
workbox.core.setLogLevel(workbox.core.LOG_LEVELS.debug);

// 只展示 log, warning 和 error 類型的 log
workbox.core.setLogLevel(workbox.core.LOG_LEVELS.log);

// 只展示 warning 和 error 類型的 log
workbox.core.setLogLevel(workbox.core.LOG_LEVELS.warn);

// 只展示 error 類型的 log
workbox.core.setLogLevel(workbox.core.LOG_LEVELS.error);

// 啥 log 都沒有,這個適用於線上生產環境
workbox.core.setLogLevel(workbox.core.LOG_LEVELS.silent);

workbox 插件

插件機制應該是 workbox 3 最大的改進了,在 2.x 中,現有的插件大部分功能都是直接以 API 的形式拋給開發者,讓開發者一頭霧水,現在 3.0 中每個內置插件自己封裝了之前的 API,對開發者來說專門解決了一個個獨立的問題。除此之外,workbox 3 還提供插件擴展機制以及事件鉤子可以讓開發者自己擴展插件。workbox 插件可以讓你通過操作一個請求的生命週期中的返回內容和請求內容添加其他的一些行爲。

workbox 插件通常都是在緩存策略中使用的,可以讓開發者的緩存策略更加靈活,workbox 內置了一些插件:

  • workbox.backgroundSync.Plugin: 如果網絡請求失敗,就將請求添加到 background sync 隊列中,並且在下一個 sync 事件觸發之前重新發起請求。

  • workbox.broadcastUpdate.Plugin: 當 Cache 緩存更新的時候將會廣播一個消息給所有客戶端,類似於 sw-register-webpack-plugin 做的事情。

  • workbox.cacheableResponse.Plugin: 讓匹配的請求的符合開發者指定的條件的返回結果可以被緩存,可以通過 statusheaders 參數指定一些規則條件。

  • workbox.expiration.Plugin: 管理 Cache 的數量以及 Cache 的時間長短。

可以像如下方式來使用 workbox 插件,以 workbox.expiration.Plugin 爲例:

1
2
3
4
5
6
7
8
9
10
11
12
workbox.routing.registerRoute(
    /\.(?:png|gif|jpg|jpeg|svg)$/,
    workbox.strategies.cacheFirst({
        cacheName: 'images',
        plugins: [
            new workbox.expiration.Plugin({
                maxEntries: 60, // 最大的緩存數,超過之後則走 LRU 策略清除最老最少使用緩存
                maxAgeSeconds: 30 * 24 * 60 * 60, // 這隻最長緩存時間爲 30 天
            }),
        ],
    }),
);

自定義插件

當然,workbox 也知道這些插件肯定不能滿足大家的自定義策略的要求,所以索性將整個請求生命週期中的關鍵事件以事件鉤子回調函數的方式暴露出來,也就是說,我們可以使用這些事件鉤子打造自己更加靈活的 workbox 插件。一個插件就是一個構造函數,而鉤子就是以構造函數的方法的形式供開發者們自定義開發,下面介紹一下有哪些事件鉤子:

cacheWillUpdate

cacheWillUpdate({request, response}),在請求返回結果替換 Cache 結果之前被調用,你可以在這個事件鉤子中在更新緩存之前修改返回的結果,如果將結果直接設爲 null 來避免當前請求的返回的結果被更新到緩存。

cacheDidUpdate

cacheDidUpdate({cacheName, request, oldResponse, newResponse}),當有新的緩存寫入記錄或者 Cache 緩存被更新時被調用,你可以調用這個方法在 Cache 緩存更新之後乾點什麼。

cachedResponseWillBeUsed

cachedResponseWillBeUsed({cacheName, request, matchOptions, cachedResponse}),在 Cache 緩存的結果在 fetch 事件中被觸發響應返回之前調用,可以使用這個回調來允許或阻止正在使用的 Cache 響應返回。

requestWillFetch

requestWillFetch({request}),當任何 fetch 事件被觸發的時候都會被調用,可以在這個毀掉中修改請求的 request 內容。

fetchDidFail

fetchDidFail({originalRequest, request}),當 fetch 事件觸發失敗的時候被調用,fetch 事件觸發失敗是網絡根本無法請求,而不是請求返回爲 非 200 的狀態的時候,可以用這個鉤子在檢測到斷網的時候乾點什麼。

如果你想寫一個自己的 workbox 插件,只需要參照如下方式:

1
2
3
4
5
6
7
8
// my-workbox-plugin.js
export default const MyWorkboxPlugin = {
    cacheWillUpdate({request, response}) {
        // ... 搞事情
    },

    // ... 繼續搞事情
};

本文參考了 Google 官方文檔,在文檔的基礎上做了一些自己的解讀和總結,有興趣的開發者可以閱讀原始文檔,文檔中還列出了一些「最佳實踐」以及 「debug」 的方式,可以幫助我們更好的使用 workbox。

歡迎大家掃碼關注我的微信公衆號「江哥亂談」,關注我更多的文章動態。

 

轉載來源:https://zoumiaojiang.com/article/amazing-workbox-3/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章