放送中

Progressive Web Apps 的一個重要面向是,其穩定性;因此,即使網路狀況不佳,也能快速載入素材資源、持續吸引使用者互動並提供意見回饋。怎麼會這樣?感謝 Service Worker fetch 事件。

擷取事件

瀏覽器支援

  • Chrome:40.
  • Edge:17。
  • Firefox:44。
  • Safari:11.1.

資料來源

fetch 事件可讓我們攔截 PWA 在 Service Worker 範圍內發出的所有網路要求,包含相同來源和跨來源的要求。除了導覽和資產要求外,如果從已安裝的 Service Worker 擷取網頁,那麼在網站初次載入後不需透過網路呼叫轉譯網頁。

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

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

您的服務工作處理程序可以將要求轉送至網路、以先前快取的回應做為回應,或是建立新回應。選擇權完全操之在您。 以下提供一個簡單的範例:

self.addEventListener("fetch", event => {
    console.log(`URL requested: ${event.request.url}`);
});

回應要求

收到要求傳入 Service Worker 時,你可以採取以下兩種做法:您可以忽略它讓它進入網路,或對其進行回應。您可以從 Service Worker 內回應要求的方式,以及如何選擇內容和傳回 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 接收的要求是否出現在快取中,如果如果是的話,則會回應 respondWith()。 做法是先搜尋快取,頂層 caches 介面提供的 match() 函式會搜尋來源或單一開啟的快取物件中的所有商店。

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,並透過規則運算式或網址格式剖析。(在本文撰寫期間,並非所有平台都支援網址格式)。

最常見的策略如下:

先快取
先搜尋快取回應,如果找不到,就會回到網路。
聯播網優先
會先要求網路的回應,如果系統未傳回任何回應,請在快取中檢查回應。
重新驗證時過時
從快取提供回應,在背景中要求最新版本,並將其儲存至快取,以供下次要求素材資源時參考。
僅限網路
一律回覆網路的回應或發生錯誤。系統一律不會參考快取資料。
僅限快取
一律回應快取或錯誤的回應。網路一律不會獲得諮詢必須將採用這項策略提供的資產加入快取,才能收到要求。

先快取

採用這項策略,Service Worker 會在快取中尋找相符的要求,如果快取成功,就會傳回對應的 Response。否則,它會從網路擷取回應 (可以選擇更新快取,供日後的呼叫使用)。如果沒有快取回應或網路回應,則要求會發生錯誤。由於在未前往聯播網的情況下放送素材資源,速度通常會比較快,因此比起更新間隔,這項策略會優先提高成效。

快取優先策略

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 的快取資產並不容易。雖然過時的策略在重新驗證策略只是其中一種做法,但這不是唯一的做法。在「更新章節」中,您將瞭解各種必要技巧,確保應用程式的內容和資產隨時保持最新狀態。

資源