這個策略算是目前應用場景最為廣泛的。主要目的就是能「從cache中盡快的獲得用戶所需要的資源」,並且「同時也透過網路去fetch該資源」,若fetch回來的資源為較新的版本,則取代從cache中獲得的資源來傳給用戶。(可以把它想成是Network with Cache Fallback Strategies的進階版。因為當網路不穩定時,用戶不在需要等待過長的時間)
來看一下在我的PWA project要怎麼實現這個策略吧:(首先這邊不是在service woker中撰寫,而是要在feed.js中來存取cache)
var url = 'https://httpbin.org/get'; // 我們就是要從該外部server的API獲取資源
var networkDataReceived = false;
fetch(url).then(function(res) {
return res.json();
}).then(function(data) {
networkDataReceived = true;
console.log('From Web', data);
clearCards();
createCard();
});
if('caches' in window) {
caches.match(url).then(function(response) {
if(response) {
return response.json();
}
}).then(function(data) {
console.log('From Cache', data);
if(!networkDataReceived) {
clearCards();
createCard();
}
});
}
function clearCards() {
while(sharedMomentsArea.hasChildNodes()) {
sharedMomentsArea.removeChild(sharedMomentsArea.lastChild);
}
}
這邊說明一下我的程式邏輯,一開始先用fetch API來獲取外部server的資源,並同時確認caches是否在window object中,如果瀏覽器有支援就到cache中尋找是否有相對應已經暫存的資源,有的話就回傳。
這邊為了避免我同時成功地從「cache」和「網路上」獲取該資源,重複執行了createCards(),我使用networkDataReceived這個布林變數來去做判斷。當fetch API先回傳我們要的資源,我們只需要針對這個回傳的資料來建立我們的card(貼文)。就算cache裡存在我們要的資源也不必再建立一個card。
再來就是我新增了一個clearCards() function,並加在createCards()前。這樣每次載入我的PWA時,都會全部重新刪掉這些cards,避免累加重複的cards越來越多,之後再將fetch回來的資源重新建立cards。
眼尖的人可能會發現,前面流程圖的第4和5步驟還沒有寫RR(也就是在service worker中針對fetch回來的資源使用dynamic caching)。那我們接著就來看一下要怎麼在sw.js file中加入這段邏輯吧!!
// Cache then network
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open(CACHE_DYNAMIC_NAME).then(function(cache) {
return fetch(event.request).then(function(res) {
cache.put(event.request, res.clone());
return res;
});
})
);
});
一樣地在service worker監聽fetch event時,將透過網路fetch回來的response,使用cache.put()添加到dynamic cache中,並回傳該response。
不過,當offline時畫面結果卻是....Why??
仔細看一下我剛在service worker中寫的那段code,其實我並沒有寫service worker要到cache中獲取資源的程式碼QQ,我只是put進去後就回傳了。
也就是說我們所有的fetch request被service worker攔截後,如果今天在沒有網路連線的情形下,它去fetch所有不管是「App shell中的靜態資源」還是「動態資源」都會失敗。
不過這也正是cache than network的使用場景,它會在網路正常連線時,快速的從cache中獲取該資源給用戶。但這個策略流程並不能在沒有網路連線時,從cache中獲取資源。
為了在離線時也能正常運作,這邊要再修改剛剛寫的code。我要將「cache then network strategies」和之前所寫的「cache with network fallback strategies」根據不同的request來使用不同的策略:
// Cache then Network
self.addEventListener('fetch', function(event) {
var url = 'https://httpbin.org/get';
if(event.request.url.indexOf(url) > -1) {
event.respondWith(
caches.open(CACHE_DYNAMIC_NAME).then(function(cache) {
return fetch(event.request).then(function(res) {
cache.put(event.request, res.clone());
return res;
});
})
);
} else {
event.respondWith(
caches.match(event.request).then(function(response) {
if(response) {
return response;
} else {
return fetch(event.request).then(function(res) {
return caches.open(CACHE_DYNAMIC_NAME).then(function(cache) {
cache.put(event.request.url, res.clone());
return res;
})
}).catch(function(err) {
return caches.open(CACHE_STATIC_NAME).then(function(cache) {
return cache.match('/offline.html');
});
});
}
})
);
}
});
這邊我將「要去fetch外部server資料的request」才使用「cache then network strategies」,而其他的request則是使用我們之前所寫的「cache with network fallback strategies」。這樣的話當offline時,在cache中的App shell靜態資源或是其他動態資源就都可以回傳了。
Day12 結束!!