iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 17
4
Modern Web

30 天 Progressive Web App 學習筆記系列 第 17

Day 17 - 30 天 Progressive Web App 學習筆記 - Service Worker - Handling Fetch Requests

  • 分享至 

  • xImage
  •  

今天筆記內容是要來了解 Service Worker 的 fetch 事件。

  • fetch 事件的觸發時機
  • 如何攔截 Requests?
  • 如何處理攔截到的 Request/Response?
    • 將每一次 response 做 cache,才可以在離線的時候做存取
    • 將 cached 過的 response 資料做回傳
    • 如果收到的 request 沒有被 cache 過,則用 fetch 把 HTTP request 真的送出,在 response 後進行 cache

fetch 事件的觸發時機

  1. Service Worker 要成功被註冊
  2. 必須要等到 activate 執行完,才會監聽 fetch 事件。
  3. 網頁上要有 request 送出

第一次進入網站,因為 Service Worker 還沒有被註冊,而我們的 request 會在 Service Worker 註冊完成進入到 activate 狀態之前就發送,所以通常第一次進入網站不會攔截到任何的 fetch 事件。


我們來簡單實作看看,首先要註冊 Service Worker,接著發送 request,Service Worker 的 fetch 事件才有辦法接收到 request 資料。

HTML - index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>PWA Demo</title>
</head>
<body>
    <p> \{^_^}/ hi! This's PWA Demo. </p>
    <ul id="list"></ul>
    <script>
		    // 註冊 Service Worker
        if ('serviceWorker' in navigator) {
            navigator.serviceWorker.register('/sw.js');
        }
    </script>
    <script>
        const list = document.getElementById('list');
        // 接著發送 request
        fetch('http://localhost:3000/people')
        .then(res => {
            return res.json();
        })
        .then(json => {
            list.innerHTML = json
                            .map(item => `<li>${item.name}</li>`)
                            .join('');
        })
    </script>
</body>
</html>

在範例 index.html 的檔案裡、寫了一段 script 載入 Service Worker 檔案去註冊 Service Worker,並且執行 fetch http://localhost:3000/people 送出 HTTP request。


如何攔截 Requests?

到 fetch 事件裡面去 console 對應的 request 資訊

// fetch
self.addEventListener('fetch', event => {
	console.log('now fetch!');
	console.log('event.request:', event.request);
	console.log('[ServiceWorker] Fetch', event.request.url);
});  

查看 console,已成功取得 request 資訊,代表網頁有送出 HTTP request:

藉由 Service Worker 的 fetch 事件,我們可以成功攔截到 request 相關資訊。


如何處理攔截到的 Request/Response?

先來講一下為什麼要處理攔截到的 Request/Response?

我們攔截到 Request 之後,預期會回傳 Response,
而必須將每一次的 Response 做 cache,才可以在離線的時候做存取,達成 PWA 的目標,所以在 fetch 的事件中,都要去處理 Request 並回傳 Response。

如何回傳 Response?

Request 要透過 respondWith 方法、才能將 response 回傳給網頁。

例如

// fetch
self.addEventListener('fetch', event => {
	event.respondWith(
		// magic goes here
	);
});  

如何將 cached 過的 response 資料做回傳

如果網站失去網路連線,我們可以回傳 cached 過的 response,提供網站資料、讓使用者能夠持續進行瀏覽,所以要如何將 cached 過的 response 資料做回傳?

我們需要透過 caches.match() 這個方法裡面帶一個參數,放入 request,就可以去配對 event.request。

範例程式碼如下:

this.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
  );
});  

找到哪一個 cache 是已被 cached 之後,接著再回傳對應的 response。

this.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function (response) {
		  return response;
    });
  );
}); 

如果收到的 request 沒有被 cache 過,則用 fetch 把 HTTP request 真的送出,在 response 後進行 cache

假如在下列的範例程式碼中,回傳的 response 是 undefined,那麼代表這個 request 沒有被 fetch 過。

因為我們必須事先保存資料,讓網站即使在離線的狀態下,仍然能夠進行瀏覽,所以要用 fetch 把 HTTP request 真的送出,在 response 後進行 cache。

self.addEventListener('fetch', event => {
	const dataUrl = 'http://localhost:3000';
	event.respondWith(
		caches.match(event.request).then(function (response) {
			return response || fetch(event.request).then(res =>
				// 存 caches 之前,要先打開 caches.open(dataCacheName)
				caches.open(dataCacheName)
				.then(function(cache) {
					// cache.put(key, value)
					// 下一次 caches.match 會對應到 event.request
					cache.put(event.request, res.clone());
					return res;
				})
			);
		})
	);
}); 

今日小結

去判斷 Request 有沒有被 cached ? 做對應處理如下,確保網站能夠離線運作。

  • 如果已經被 cached:則回傳 cache 裡的 Response
  • 若沒有被 cached:則進行 cache 的動作再回傳 Response

參考文件


本人小小筆記,如有錯誤或需要改進的部分,歡迎給予回饋。
我將會用最快的速度修正,m(_ _)m。謝謝

上一篇
Day 16 - 30 天 Progressive Web App 學習筆記 - Service Worker - Clearing Old Cache
下一篇
Day 18 - 30 天 Progressive Web App 學習筆記 - To-Do List 實作 PWA - Application Shell 與靜態檔案的離線存取
系列文
30 天 Progressive Web App 學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言