Progressive Web App Service Worker 讓網頁支援離線快取及推播

me
林彥成
2021-09-05 | 3 min.
文章目錄
  1. 1. 什麼是 Service Workers
  2. 2. Service Workers 常見用途
  3. 3. 註冊與使用 Service Worker
  4. 4. PWA 推播
  5. 5. Workbox

什麼是 Service Workers

Service Workers 的角色是位於 Web App 與網路連接中間的一層 “代理網路連接”,就像是秘書一樣負責將資料處理後再轉介給 App。

還記得第一次看到這個名詞是因為看 Progressive Web App,這裡比較新的觀念是可以幫助 web app 在網路較慢或是沒有連線時去使用快取資料。另外一個特性是 service worker 可以在頁面沒開啟時背景執行,由於可以背景執行,所以接受推播的任務也可以在這裡進行撰寫,需要注意的是推播分成兩部分實作,一部分是通知,一部分是接受推播,後端各家的概念上其實都是先針對 topic (可以想像成頻道) 進行訂閱,然後撰寫接收事件,然後觸發通知。

最終透過這樣的技術實現,這層 “代理網路連接” 讓資源能夠被快取下來且能夠讓裝置在離線時被使用,JavaScript 是單線程的語言,但是 Service Workers 會跟頁面的 thread 分開運行,且不會影響到 DOM 的結構,這個概念就像是打 API 其實也是非阻塞的執行,可以在收到資料後才進行動作,同樣的我們也可以把這樣的任務交給 Service Worker,除了離線快取以外,也能夠支援推播的處理或是讓 worker 來執行一些耗能的計算。

最後要注意的是 Service Workers 在考量安全性的基礎上,只能執行在 HTTPS 的環境。

Service Workers 常見用途

  1. 不同的快取策略運用
    • Network or Cache: 使用線上資源或使用快取
    • Cache only: 只使用快取
    • Cache and update: 使用快取並使用線上資源更新快取
    • Cache, update and refresh: 使用快取,但同時也用線上資源來更新快取,若有新資源就同時更新畫面
  2. Web Push
    • 訂閱推播
    • 推播內容樣式實作

註冊與使用 Service Worker

底下 Demo 了第一次先將圖片 icon 快取,第二次如果發現有使用到 dog 時則用 icon 取代,可以明顯看到有三個階段,實作上就是透過這幾個事件的觸發行為來達到我們的目的。

  • install: 安裝階段
  • activate: 啟用階段,可以在這個階段清除舊的快取
  • fetch: 代理網路,在這裡可以決定要回覆線上的內容或是快取的內容到 Web App 中

首先第一步是去實作相關的事件內容,在 install 階段中有個 waitUntil,白話就是必須等待安裝成功後才去執行相關事件。

再來就是 cache 這個物件該怎麼去使用,初期我們就先不考慮那麼多,先照著範例進行撰寫,那既然是快取,有個重點就是快取要有版本名稱,程式碼中我們目前就是 V1。

caches 是提供給 Service Worker 使用的特殊 CacheStorage 物件,因為 web storage 在實作上是同步機制所以無法使用,在 Service Workers 中儲存都是以 Cache API 為主。

快取的 API 相關文件: https://developer.mozilla.org/en-US/docs/Web/API/Cache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
self.addEventListener("install", (event) => {
console.log("installing…");

// cache a icon
event.waitUntil(
caches.open("v1").then((cache) => cache.add("images/icon.png"))
);
});

self.addEventListener("activate", (event) => {
console.log("ready to handle fetches!");
// 可以在這個階段清除舊的快取
e.waitUntil(
caches.keys().then((keyList) => {
Promise.all(
keyList.map((key) => {
if (key === cacheName) {
return;
}
caches.delete(key);
})
);
})
);
});

self.addEventListener("fetch", (event) => {
const url = new URL(event.request.url);

if (url.origin == location.origin && url.pathname == "/images/dog.jpg") {
console.log(url);
event.respondWith(caches.match("images/icon.png"));
}
});

最後實做完成後別忘了註冊,Service Worker 才會生效,這裡可以透過 Chrome 的內建工具來觀察:

  • chrome://inspect/#service-workers
1
2
3
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("./pwa-examples/test/sw.js");
}

PWA 推播

  1. 有支援的話,就能夠透過 Service Work 來使用推播功能 (swRegistration)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let swRegistration;

function initialiseUI() {
// Set the initial subscription value
swRegistration.pushManager.getSubscription().then(function (subscription) {
isSubscribed = !(subscription === null);

if (isSubscribed) {
console.log("User IS subscribed.");
} else {
console.log("User is NOT subscribed.");
}

updateBtn();
});
}

navigator.serviceWorker.register("sw.js").then(function (swReg) {
console.log("Service Worker is registered", swReg);

swRegistration = swReg;

initialiseUI();
});

詳細教學可以參考:
https://developers.google.com/web/fundamentals/codelabs/push-notifications?hl=zh-tw

Workbox

Workbox 由 google 開發維護,是一套用來強化 service worker 的工具包,方便我們更快實作出 production-ready 的 Service Worker,幾種常見的快取都已經提供相關範例,其實我們只需要照抄即可。

  • Cache Google Fonts
  • Cache JavaScript and CSS
  • Cache Images
  • Precache your Files
  • Offline Google Analytics

喜歡這篇文章,請幫忙拍拍手喔 🤣

share