正在提供服務

Progressive Web Apps 的一大優點在於,它們的穩定性可以快速載入,讓使用者保持互動並立即提供意見回饋。怎麼會這樣?感謝 Service Worker fetch 事件。

擷取事件

瀏覽器支援

  • 400
  • 17
  • 44
  • 11.1

資料來源

fetch 事件可讓我們攔截來自 Service Worker 範圍中 PWA 的所有網路要求,包括相同來源和跨來源要求。除了導覽和資產要求之外,如果從已安裝的 Service Worker 擷取資訊,也會允許在沒有網路呼叫的情況下,於網站首次載入後就瀏覽網頁。

fetch 處理常式會接收應用程式發出的所有要求 (包括網址和 HTTP 標頭),讓應用程式開發人員決定如何處理這些要求。

Service Worker 位於用戶端和網路之間。

服務工作處理程序可以將要求轉送至網路、使用先前快取的回應或建立新回應。一切由您決定。 以下提供一個簡易範例:

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 的快取素材資源保持在最新狀態並不容易。雖然重新驗證策略已過時,但這不是唯一的方法。「更新」章節將介紹各種技巧,讓您的應用程式內容和素材資源保持在最新狀態。

資源