Progressive Web Apps 的一大優點在於,它們的穩定性可以快速載入,讓使用者保持互動並立即提供意見回饋。怎麼會這樣?感謝 Service Worker fetch
事件。
擷取事件
fetch
事件可讓我們攔截來自 Service Worker 範圍中 PWA 的所有網路要求,包括相同來源和跨來源要求。除了導覽和資產要求之外,如果從已安裝的 Service Worker 擷取資訊,也會允許在沒有網路呼叫的情況下,於網站首次載入後就瀏覽網頁。
fetch
處理常式會接收應用程式發出的所有要求 (包括網址和 HTTP 標頭),讓應用程式開發人員決定如何處理這些要求。
服務工作處理程序可以將要求轉送至網路、使用先前快取的回應或建立新回應。一切由您決定。 以下提供一個簡易範例:
self.addEventListener("fetch", event => {
console.log(`URL requested: ${event.request.url}`);
});
回應要求
服務工作人員收到要求時,您可以採取兩項行動:忽略要求,讓其進入網路,或者您也可以回應要求。在服務工作處理程序中回應要求,就是您能夠選擇什麼方式,以及將應用程式傳回 PWA 的方式,即使使用者處於離線狀態也沒問題。
如要回應傳入的要求,請從 fetch
事件處理常式中呼叫 event.respondWith()
,例如:
// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
const response = .... // a response or a Promise of response
event.respondWith(response);
});
您必須同步呼叫 respondWith()
,且您必須傳回 Response 物件。不過,擷取事件處理常式結束後 (例如非同步呼叫中),您無法呼叫 respondWith()
。如果您需要等待完整回應,可以傳遞 Promise 至 respondWith()
,透過回應進行解析。
正在建立回應
利用 Fetch API,您可以在 JavaScript 程式碼中建立 HTTP 回應,並使用 Cache Storage API 來快取這些回應,並當做來自網路伺服器一樣的回應。
如要建立回應,請建立新的 Response
物件、設定主體及狀態、標頭等選項:
const simpleResponse = new Response("Body of the HTTP response");
const options = {
status: 200,
headers: {
'Content-type': 'text/html'
}
};
const htmlResponse = new Response("<b>HTML</b> content", options)
從快取回應
您現在已經瞭解如何從 Service Worker 提供 HTTP 回應,接著就要使用快取儲存空間介面將資產儲存到裝置上。
您可以使用 Cache Storage API,檢查從 PWA 收到的要求是否存在於快取中;如果找到,請用該 API 回應 respondWith()
。方法是在快取中搜尋。match()
函式 (位於頂層 caches
介面) 會搜尋來源中的所有商店,或搜尋單一開放式快取物件。
match()
函式會接收 HTTP 要求或網址做為引數,並傳回與對應鍵相關聯的「回應」的承諾。
// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
console.log(response ? response : "It's not in the cache");
});
// Cache-specific search
caches.open("pwa-assets").then(cache => {
cache.match(urlOrRequest).then(response => {
console.log(response ? response : "It's not in the cache");
});
});
快取策略
並非每種用途都必須透過瀏覽器快取提供檔案。舉例來說,使用者或瀏覽器可以清除快取。因此,您應自行製定以 PWA 傳送資產的策略。系統並未限定使用單一快取策略。您可以為不同的網址模式定義不同的網址。舉例來說,您可以為最低 UI 資產建立一種策略,再為 API 呼叫建立三項策略,另外再為圖片和資料網址建立第三個策略。如要這麼做,請參閱 ServiceWorkerGlobalScope.onfetch
中的 event.request.url
,然後透過規則運算式或網址格式進行剖析。(在本文撰寫的時間,並非所有平台都支援網址格式)。
最常見的策略如下:
- 先快取
- 先搜尋快取回應;如果找不到,則會改回使用網路。
- 網路第一
- 會先要求網路的回應,如果未傳回任何回應,就會在快取中檢查回應。
- 重新驗證時過時
- 從快取提供回應,在背景要求最新版本,並將該版本儲存到快取,方便下次要求資產時使用。
- 僅限網路
- 一律回覆網路訊息或錯誤。絕對不會查詢快取資料。
- 僅快取
- 一律傳回快取或錯誤的回應。不會查詢網路。使用此策略放送的素材資源必須先新增至快取,才能提出請求。
先快取
使用這項策略時,服務工作處理程序會在快取中尋找相符的要求,並於快取後傳回相應的回應。否則,它會從網路擷取回應 (可選擇更新快取以供日後呼叫使用)。如果沒有快取回應和網路回應,要求就會發生錯誤。由於在不透過網路的情況下提供素材資源會比較快,因此這項策略會優先考量效能,而非更新速度。
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request)
.then(cachedResponse => {
// It can update the cache to serve updated content on the next request
return cachedResponse || fetch(event.request);
}
)
)
});
先連上網路
此策略是快取優先策略的鏡像;它會檢查要求是否能從網路執行;如果無法執行,會嘗試從快取中擷取。先對快取功能一樣。如果沒有網路回應或快取回應,要求就會發生錯誤。從網路取得回應,通常比從快取取得回應慢,這項策略會優先顯示更新的內容,而非效能。
self.addEventListener("fetch", event => {
event.respondWith(
fetch(event.request)
.catch(error => {
return caches.match(event.request) ;
})
);
});
重新驗證時已過時
重新驗證策略時已過時的快取回應會立即傳回快取回應,然後檢查網路是否有更新。如果找到快取回應,請替換成該回應。這項策略一律會發出網路要求,因為即使找到快取資源,該策略仍會試著以網路收到的內容來更新快取中的內容,在下一個要求中使用更新版本。因此,這項策略可讓您享有快速提供快取優先策略的好處,以及在背景更新快取的優點。
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cachedResponse => {
const networkFetch = fetch(event.request).then(response => {
// update the cache with a clone of the network response
const responseClone = response.clone()
caches.open(url.searchParams.get('name')).then(cache => {
cache.put(event.request, responseClone)
})
return response
}).catch(function (reason) {
console.error('ServiceWorker fetch failed: ', reason)
})
// prioritize cached response over network
return cachedResponse || networkFetch
}
)
)
})
僅限網路
這種純網路策略類似於瀏覽器沒有 Service Worker 或 Cache Storage API 的行為。只有可從網路擷取到的資源時,要求才會傳回資源。這通常對於純線上 API 要求等資源相當實用。
僅快取
這種僅快取策略可確保要求絕不會流向網路;所有傳入要求都會以預先填入的快取項目回應。以下程式碼會使用 fetch
事件處理常式與快取儲存空間的 match
方法,只回應快取:
self.addEventListener("fetch", event => {
event.respondWith(caches.match(event.request));
});
自訂策略
雖然以上是常見的快取策略,但您必須自行負責服務工作處理程序和要求的處理方式。如果這些方法都不符合您的需求,可以自行建立。
舉例來說,您可以使用設有逾時的網路優先策略,決定更新內容的優先順序,但前提是回應必須落在您設定的門檻內。您也可以將快取回應與網路回應合併,並從 Service Worker 建構複雜的回應。
正在更新資產
讓 PWA 的快取素材資源保持在最新狀態並不容易。雖然重新驗證策略已過時,但這不是唯一的方法。「更新」章節將介紹各種技巧,讓您的應用程式內容和素材資源保持在最新狀態。