使用 Workbox 時,您可能會想在系統擷取或快取要求和回應時加以操控。透過 Workbox 外掛程式,您能以最少的樣板將其他行為新增至 Service Worker。這些套件可封裝並在您的專案中重複使用,或是公開發布供他人使用。
Workbox 提供許多立即可用的外掛程式,如果您很擅長,也可以根據應用程式的需求編寫自訂外掛程式。
可用的 Workbox 外掛程式
Workbox 提供下列官方外掛程式,供您在 Service Worker 中使用:
BackgroundSyncPlugin
:如果網路要求應失敗,這個外掛程式可讓您將其加入背景同步處理佇列,在觸發下一個同步處理事件時再次提出要求。BroadcastUpdatePlugin
:讓您在更新快取時透過廣播管道或postMessage()
分派訊息。CacheableResponsePlugin
:僅快取符合特定條件的快取要求。ExpirationPlugin
:管理快取中項目的數量與上限。RangeRequestsPlugin
:回應含有Range
HTTP 要求標頭的要求。
無論是上述其中一種外掛程式,還是自訂外掛程式,都可以與 Workbox 策略搭配使用,方法是將外掛程式的執行個體新增至策略的 plugins
屬性:
import {registerRoute} from 'workbox-routing';
import {CacheFirst} from 'workbox-strategies';
import {ExpirationPlugin} from 'workbox-expiration';
registerRoute(
({request}) => request.destination === 'image',
new CacheFirst({
cacheName: 'images',
plugins: [
new ExpirationPlugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
}),
],
})
);
自訂外掛程式的方法
Workbox 外掛程式需要實作一或多個回呼函式。當您將外掛程式新增至策略時,系統會自動在適當時機執行回呼函式。策略會傳送回呼函式目前要求和/或回應的相關資訊,讓外掛程式瞭解需要採取行動的背景。系統支援下列回呼函式:
cacheWillUpdate
:在使用Response
更新快取之前呼叫。在此方法中,您可以在回應加入快取之前變更回應,或是傳回null
,避免完全更新快取。cacheDidUpdate
:在快取中新增項目或更新現有項目時呼叫。想要在快取更新後執行動作時,使用這個方法的外掛程式可能會派上用場。cacheKeyWillBeUsed
:在要求用做快取金鑰前呼叫。這適用於快取查詢 (當mode
為'read'
) 和快取寫入 (當mode
為'write'
時)。如果您需要先覆寫網址或將其正規化,再使用網址存取快取,這個回呼就十分實用。cachedResponseWillBeUsed
:系統會在使用快取的回應之前呼叫此方法,可讓您檢查回應。目前,您可以選擇傳回其他回應,或是傳回null
。requestWillFetch
:每當要求傳送至網路時呼叫。需要在進入網路之前變更Request
時相當實用。fetchDidFail
:在網路要求失敗時呼叫;最有可能的原因是沒有網路連線,不會在瀏覽器連上網路,卻收到錯誤訊息 (例如404 Not Found
) 時觸發。fetchDidSucceed
:每當網路要求成功時呼叫,無論 HTTP 回應代碼為何。handlerWillStart
:在任何處理常式邏輯開始執行前呼叫。如果需要設定初始處理常式狀態,相當實用。舉例來說,如果您想瞭解處理常式產生回應所需的時間,可以記下這個回呼中的開始時間。handlerWillRespond
:在策略的handle()
方法傳回回應之前呼叫。如果您需要修改回應,然後再將其傳回RouteHandler
或其他自訂邏輯,這個方法就非常實用。handlerDidRespond
:在策略的handle()
方法傳回回應之後呼叫。這有助於記錄任何最終回應詳細資料 (例如其他外掛程式所做的變更後)。handlerDidComplete
:在觸發策略時為事件新增的所有延長生命週期承諾後呼叫。當您需要記錄任何需要等待處理常式完成才能計算資料 (例如快取命中狀態、快取延遲、網路延遲和其他實用資訊) 的資料時,這項功能就非常實用。handlerDidError
:如果處理常式無法提供來自任何來源的有效回應,則呼叫此;提供某種備用回應,做為失敗的替代方案。
這些回呼都是 async
,因此每當快取或擷取事件到達相關回呼的相關時間點時,都必須使用 await
。
如果外掛程式使用上述所有回呼,則會產生程式碼:
const myPlugin = {
cacheWillUpdate: async ({request, response, event, state}) => {
// Return `response`, a different `Response` object, or `null`.
return response;
},
cacheDidUpdate: async ({
cacheName,
request,
oldResponse,
newResponse,
event,
state,
}) => {
// No return expected
// Note: `newResponse.bodyUsed` is `true` when this is called,
// meaning the body has already been read. If you need access to
// the body of the fresh response, use a technique like:
// const freshResponse = await caches.match(request, {cacheName});
},
cacheKeyWillBeUsed: async ({request, mode, params, event, state}) => {
// `request` is the `Request` object that would otherwise be used as the cache key.
// `mode` is either 'read' or 'write'.
// Return either a string, or a `Request` whose `url` property will be used as the cache key.
// Returning the original `request` will make this a no-op.
return request;
},
cachedResponseWillBeUsed: async ({
cacheName,
request,
matchOptions,
cachedResponse,
event,
state,
}) => {
// Return `cachedResponse`, a different `Response` object, or null.
return cachedResponse;
},
requestWillFetch: async ({request, event, state}) => {
// Return `request` or a different `Request` object.
return request;
},
fetchDidFail: async ({originalRequest, request, error, event, state}) => {
// No return expected.
// Note: `originalRequest` is the browser's request, `request` is the
// request after being passed through plugins with
// `requestWillFetch` callbacks, and `error` is the exception that caused
// the underlying `fetch()` to fail.
},
fetchDidSucceed: async ({request, response, event, state}) => {
// Return `response` to use the network response as-is,
// or alternatively create and return a new `Response` object.
return response;
},
handlerWillStart: async ({request, event, state}) => {
// No return expected.
// Can set initial handler state here.
},
handlerWillRespond: async ({request, response, event, state}) => {
// Return `response` or a different `Response` object.
return response;
},
handlerDidRespond: async ({request, response, event, state}) => {
// No return expected.
// Can record final response details here.
},
handlerDidComplete: async ({request, response, error, event, state}) => {
// No return expected.
// Can report any data here.
},
handlerDidError: async ({request, event, error, state}) => {
// Return a `Response` to use as a fallback, or `null`.
return fallbackResponse;
},
};
上述回呼中可用的 event
物件是觸發擷取或快取動作的原始事件。有時「並非」原始事件,因此在參照事件之前,程式碼應先檢查是否存在。
所有外掛程式回呼也會傳遞 state
物件,此物件專屬於特定外掛程式及其叫用的策略。也就是說,您可以編寫外掛程式,讓其中一個回呼根據相同外掛程式中另一個回呼的特性,有條件地執行工作 (例如計算執行 requestWillFetch()
與 fetchDidSucceed()
或 fetchDidFail()
之間的差異)。
第三方外掛程式
如果您開發了某個外掛程式,且認為該外掛程式在專案外使用,建議您以模組的形式發布外掛程式!以下簡要列出社群提供的 Workbox 外掛程式:
cloudinary-workbox-plugin
,會根據目前的連線速度,動態重新編寫 Cloudinary 託管圖片的要求。workbox-plugin-firebase-auth
可協助為需要 Firebase 驗證的傳出要求管理Authorization: Bearer
。
你可以在 npm 存放區中搜尋,找到更多社群提供的 Workbox 外掛程式。
最後,如果您已經建立了想要分享的 Workbox 外掛程式,請在發布時加入 workbox-plugin
關鍵字。如有興趣,請透過 Twitter @WorkboxJS 告訴我們!