Progressive Web Application Day04 — 好的快取讓使用者上天堂 Part 1

Jacky810124
8 min readMar 21, 2018

--

Progressive Web Application

上一篇文章中( Progressive Web Application Day03 — 隱藏工具人 Service Worker )簡單介紹了什麼是隱藏工具人 Service Worker ,這篇將會著重在:如何使用 Service Worker 快取頁面中所需要的檔案

另外,這篇會比較偏向動手做系列文章,因此會附上完整程式碼,如果有興趣的朋友,可以到以下網址查看完整程式碼。

https://github.com/jacky810124/pwa-day04

為什麼要快取呢?

俗話說:沒圖沒真相,先來個實際比較圖

實際比較圖

雖然在時間的部分,差異並不是很大,但在資料傳輸量就差了 349KB ,這還只是單純只是一個簡單頁面的靜態檔案,如果是實際的網站,可能還會有不少圖片,傳輸量和時間的差距會更大。

在有被快取的情況,可以看到在 Size 的欄位,幾乎全部的檔案都是由 Service Worker 所提供的。這代表了檔案已經被儲存在 Cache Storage ,使用者再次瀏覽這個網頁時,不需要再耗費額外的流量去遠端 Server 拿檔案,同時頁面讀取的時間也會變短。

快取,真的好棒棒!

快取,真的是好棒棒

如何快取?

在實際動手做快取之前,有幾個角色必須要先認識:

  • Service Worker :隱藏工具人,執行在背景,可以默默的替你處理事情。
  • Cache Storage :透過 Key-Value 的形式儲存 Request 和 Response 。

對這幾個角色有基本的認識後,接下來就可以開始動手做了!

Step.1 創建專案

專案的結構大概會長得像這樣

.
├── app.js
├── icons
│ ├── android-icon-144x144.png
│ ├── android-icon-192x192.png
│ ├── android-icon-48x48.png
│ ├── android-icon-96x96.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ └── favicon.ico
├── index.html
├── logo.png
├── manifest.json
└── sw.js
  • index.html — 主要的 HTML 檔。
  • app.js — 主要的 JavsScript 程式。
  • sw.js — Service Worker 的程式。

Step.2 註冊 Service Worker

在開始快取之前,必須先註冊 Service Worker ,註冊完成之後,如果 Service Worker 都有被正確的下載與解析,接下來就會觸發 install 的事件,在這個生命週期中,非常適合拿來快取網頁中所需要的靜態檔案。

謎之聲:如果對 Service Worker 還不熟悉的朋友,把上個章節看個 10 遍啊

接著要在 app.js 中註冊 Service Worker ,不過因為 Service Worker 還屬於比較新的功能,因此必須要先檢查瀏覽器是否支援,透過 'serviceWorker' in navigator 可以檢查是否支援。

如果瀏覽器支援,接下來就可以開始註冊 Service Worker ,可以透過 navigator.serviceWorker.register('/sw.js') 註冊,註冊完成之後,接下來就可以透過 Service Worker 快取網頁中的檔案了。

app.js 完整的程式碼會長得像這樣:

app.js

底下也附上在 2018–03–22 這天的 Service Worker 支援度。

Can I Use — Service Worker

Step.3 App Shell

在開始快取之前,必須要了解哪些是屬於 App Shell 。

為什麼呢?

因為 Cache Storage 是有容量的限制,這個會根據瀏覽器的不同,會有不同的限制,但是身為工程師,你有責任把快取給管理好,因此不能把所有的東西都往 Cache Storage 裡面塞。

App Shell 指的是Web App 的殼,也就是頁面上必要的東西。即便今天使用者沒有網路連線了,透過 Cache Storage 裡面所儲存的 App Shell ,也要能夠正常瀏覽。

如果以完整程式碼範例中的 index.html 為例,屬於 App Shell 的部份大概有:

整理出來之後,就可以開始把這些東西都加進 Cache Storage 了!

Step.4 快取

在 sw.js 裡面可以開始新增一些有關快取的程式碼,在上篇文章中有提到,在 install 這個生命週期中,非常適合快取頁面中所需要的檔案。

因此,在 sw.js 需要使用 self.addEventListener('install', event => {}) 來監聽 install 的事件,而快取的邏輯則是寫在這個事件處理中。

完整的程式碼大概會長得下這樣:

sw.js

看到一大片程式碼覺得慌嗎?別擔心,一步步來看。

謎之聲:在 Service Worker 中,會使用大量的 Promise 作為非同步的流程控制,因此如果對於 Promise 還不很熟,建議先把 Promise 補起來,再進行接下來的部分。

在事件處理中,可以看到 event.waitUntil 這個方法,這個方法主要是用來避免 Service Worker 在快取還沒完成前,就進入 terminated 的狀態,因此透過這個方法可以告訴 Service Worker ,還有事情還沒結束,必須等到結束後才能進入 terminated 。

接著是 self.caches.open(APP_ASSETS_CACHE_NAME) ,這個部分則是在 Cache Storage 中,取得一個符合 APP_ASSETS_CACHE_NAME 的 Cache object,透過 Promise 的 then 則可以接收到這個 Cache object。

cache.addAll 就可以把剛剛整理出來的 App Shell 路徑,通通快取起來。

因此整個快取的流程,簡單來說就是:

  1. 接收到 install 事件
  2. 取得符合 APP_ASSETS_CACHE_NAME 的 Cache object
  3. 儲存到 Cache Storage
簡易快取流程

做到這邊,整個快取的流程就差不多了,但是開啟網站後,會發現不管重新整理多少次,還是會去跟遠端 Server 去要資料,到底有沒有把檔案快取起來啊?

可以透過 Chrome 的開發者工具,切換到 Application 分頁後展開 Cache Storage ,選擇 app-assets-cache-v0.0.1 ,應該可以看到類似的畫面。

Cache Storage in Chrome Dev Tool

這個畫面代表了,的確有把東西存進 Cache Storage ,但是當頁面去發 Request 時,並不知道在 Cache Storage 裡已經有了,因此還是會再次去跟遠端 Server 拿資料。

下個章節,來改善這個問題!

後記

啊啊啊啊,這篇文章也寫好久啊,發現動手做系列的文章,需要花費更長的時間,覺得一天一篇文章好像會很容易難產,不知道在文章中附上程式碼說明與完整的程式碼大家會不會更容易了解?

好想工作室Ken 在 2018–03–22 13:30 也會分享「建構 PWA」—如何建構 PWA ,有興趣的朋友也可以抽空前往參加活動。

2018–03–15 在好想工作室有分享 PWA — 簡單介紹 這個主題,雖然活動已經結束,但還是放上活動簡報,如果對於 PWA 有興趣的話,都歡迎聯絡我或是在底下留言給我。

--

--