這邊我舉幾個實際可能發生的例子:
這裡有一件重要的事情,當我們使用網頁應用時,實際上有多個暫存可供使用。瀏覽器本身也內建暫存,但這個由瀏覽器管理的暫存有一個很大的缺點,它完全是由瀏覽器管理,我們開發人員不能使用它(不能明確告訴它哪些資源要暫存,哪些資源不暫存)。
因此,作為開發人員,我們要使用的就是Cache API。這個cache storage也是存在於瀏覽器中,但是可以由開發人員進行管理。
這個Cache包含了簡單的key-value pair,其中key是「要發送的HTTP Request」(可能是網頁要求的外部圖片資源或是嘗試訪問的URL),而value是「取得的HTTP Response」。用戶至少需要成功地傳送過一次請求才能得到想要顯示的response,否則當然就無法暫存它。
所以當用戶下一次需要再次發送相同的request但沒有網路連線時,發送請求的response會從Cache中獲得,這就是Cache API大致的運作方式。
BTW,我們可以從「service worker」和「普通的Javascript Code」來使用Cache API,不過目前我還是先在service worker中來去執行caching的工作
目前project實作到這邊,我新增一個fake的貼文,以便我們測試用,因為現在我還沒有實作出後台資料庫、拍照和或取得地理位置等等這些功能,這之後會再補上。
在feed.js中新增下列的code:
var sharedMomentsArea = document.querySelector('#shared-moments');
function createCard() {
var cardWrapper = document.createElement('div');
cardWrapper.className = 'shared-moment-card mdl-card mdl-shadow--2dp';
var cardTitle = document.createElement('div');
cardTitle.className = 'mdl-card__title';
cardTitle.style.backgroundImage = 'url("/src/images/sf-boat.jpg")';
cardTitle.style.backgroundSize = 'cover';
cardTitle.style.height = '180px';
cardWrapper.appendChild(cardTitle);
var cardTitleTextElement = document.createElement('h2');
cardTitleTextElement.className = 'mdl-card__title-text';
cardTitleTextElement.textContent = '台北板橋一日遊';
cardTitle.appendChild(cardTitleTextElement);
var cardSupportingText = document.createElement('div');
cardSupportingText.className = 'mdl-card__supporting-text';
cardSupportingText.textContent = '在台北板橋';
cardSupportingText.style.textAlign = 'center';
cardWrapper.appendChild(cardSupportingText);
componentHandler.upgradeElement(cardWrapper);
sharedMomentsArea.appendChild(cardWrapper);
}
// 「https://httpbin.org/get」這段之後會改成我們的server
fetch('https://httpbin.org/get').then(function(res) {
return res.json();
}).then(function(data) {
createCard();
});
畫面結果:
這邊因為不是我們本篇的重點,我只稍微說明一下,createCard function我直接用Javascript創建一些html element並把值寫死,來假裝新增的貼文。
另外下面為什麼要先fetch API之後才創建我們的貼文?主要原因是之後我會建一個server,來儲存我們的貼文資料。所以流程上應該是先從server上取回貼文資料後再呼叫createCard顯示到前端。
這裡就要先了解一個名詞「App Shell」,根據google developer web的定義:
App“shell”是支援用戶界面所需最小的HTML、CSS和JavaScript,如果離線暫存,可確保在用戶重複訪問時提供即時、可靠的良好效能。這意味着並不是每次用戶訪問時都要從網路載入App Shell,只需要從網路中載入必要的內容。
以我的PWA project來說的話,App shell包括了載入index.hmtl後導入的一些像是「material design CDN」、「google font CDN」和其他內部的一些CSS和JS file,這些都是確保我們頁面中像是導覽條、plus icon...等能正常顯示的主要「靜態檔案」(這裡所說的靜態檔案是指比較不常進行更改的檔案)。
當我們確定project中那些資源是app shell之後,就可以準備開始在service worker的「install階段」pre-cache這些靜態的檔案囉!!
為何要在install階段?因為之前有說過一旦sw.js更改了,service worker就會重新install並activate,所以我希望每次有新版本的service worker時,都會再去重新pre-cache我的PWA中app shell的部分。
開始在我們的PWA project中pre-cache app shell吧:
一開始主要就是在service worker install階段使用cache api,pre-cache這幾個檔案(當然之後還會再增加)。讓用戶就算今天在沒有網路連線的情況下,仍能正常瀏覽index.html。
現在我的sw.js中監聽install event的function變成:(這裡我只先示範暫存app.js檔案)
self.addEventListener('install', function(event) {
console.log('[Service Worker] Installing Service Worker ...', event);
event.waitUntil(
caches.open('static').then(function(cache) {
console.log('[Service Worker] Precaching App Shell');
cache.add('/src/js/app.js');
})
);
});
cache.open('static')會在瀏覽器的Cache Storage中新增一個名為「static」的sub cache,open()會回傳這個sub cache的promise。接著我們就可以用cache.add(網址路徑)來發出request並將請求結果暫存到cache中。
至於為何我們要將暫存動作放在event.waitUntil()裡呢?答案就是我之前有提到在service worker中程式的執行都是「非同步的」,所以為了避免當cache動作還在執行時,底下已經開始要fetch cache資源。這裡我使用event.waitUntil()方法強制等到cache動作執行結束後,才能繼續執行接下來的步驟。
Day08 結束!!