iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 8
1

為什麼我們的PWA需要Caching?


這邊我舉幾個實際可能發生的例子:

  • 想一想如果今天我們的網站有大量用戶湧入時。用戶可能會在「網路連線不良」的情形下,仍然想閱讀某篇新聞文章並導向到另一篇文章的事情,但網頁卻顯是無法連線因為它沒有網路連接線。但是我們知道對於service worker來說,它是一個background process,所以我們可以透過service worker預先來暫存某些資源,讓用戶即使網路不良的情況下,仍然能正常瀏覽我們的網頁。
  • 另外,當用戶在地下室或是電梯這種「沒有網路」的環境中,為了讓他們在這段時間也能夠造訪我們的網頁,我們一樣需要透過service worker來完成暫存網頁的某些資源。

了解Cache API


這裡有一件重要的事情,當我們使用網頁應用時,實際上有多個暫存可供使用。瀏覽器本身也內建暫存,但這個由瀏覽器管理的暫存有一個很大的缺點,它完全是由瀏覽器管理,我們開發人員不能使用它(不能明確告訴它哪些資源要暫存,哪些資源不暫存)。

因此,作為開發人員,我們要使用的就是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顯示到前端。

那我們要預先Cache(Pre-cache)網頁中的那些資源哩?

這裡就要先了解一個名詞「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這些靜態的檔案囉!!/images/emoticon/emoticon14.gif

為何要在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 結束!! /images/emoticon/emoticon41.gif


上一篇
[Day07] 複習一下Jacvascript中的 Promise 和 Fetch(Part2)
下一篇
[Day09] 在Service Worker中實作Caching(Part2)
系列文
你應該要知道的新一代Web技術---漸進式網頁(PWA)29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言