Progressive Web App 定期背景同步 Periodic Background Sync API 入門實做

me
林彥成
2021-09-23 | 2 min.
文章目錄
  1. 1. 什麼是 Periodic Background Sync API
  2. 2. 怎麼使用定期背景同步資料

什麼是 Periodic Background Sync API

透過在 service worker 搭配 Periodic Background Sync API 就可以做到定期背景同步資料的效果。

背景同步資料可以解決什麼問題,Progressive Web App 說穿了也只是網站,所以當遇到連線不良的時候總是會影響操作體驗。

一個正常的使用者歷程:

  1. 從口袋裡拿出手機
  2. 使用一下 App 達成目的
  3. 關閉手機

在這樣的歷程中,尤其在連假坐火車返鄉時,網頁經常因為網路不良而轉圈圈或中斷。如果是重要的訊息我們會願意等待一段時間後又再次重新整理,但很可能又再次落空,這樣的體驗會是我們想要的嗎?

Service Worker 雖然讓用戶可以透過快取內容來解決網路問題,但是當頁面需要新資料的時候呢?在沒有任何處置前,我們只會收到網路不穩連線逾時請重試,這依舊是一個不好的體驗,透過定期背景同步資料的 API 可以優化這樣的體驗。

優化過後的使用者歷程會可能像下面這樣:

  1. 閱讀任何文章
  2. 斷線
  3. 點擊另一篇文章
  4. 知頁面載入失敗
  5. 同意通知
  6. 關閉瀏覽器 (App)
  7. 恢復網路
  8. 當文章下載、快取後並準備好時就會收到通知

以上的情境 Google 有提供舊版 API 的教學並提供的 Demo 影片如下:

怎麼使用定期背景同步資料

早期是透過 SyncManager.register() 可以做到,不過這樣的做法直到目前都還不是標準,也不建議被使用,不過漸進式增強的寫法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
if ("serviceWorker" in navigator && "SyncManager" in window) {
navigator.serviceWorker.ready
.then(function (reg) {
return reg.sync.register("tag-name");
})
.catch(function () {
// 註冊 SyncManager 失敗
postDataFromThePage();
});
} else {
// 不支援
postDataFromThePage();
}

另外一個支援度較高的方式是透過 ServiceWorkerRegistration.periodicSync.register()

相關標準如連結: https://wicg.github.io/periodic-background-sync/

使用上的流程也是不難,只要瀏覽器有這個 API 可以使用就可以用,不過為了防呆還是建議寫上 try catch。

1
2
3
4
5
6
7
8
// 先抓出來 registration
const registration = await navigator.serviceWorker.ready;

// 判斷有沒有這個 API
if ("periodicSync" in registration) {
// 拿出來會是一個物件
const periodicSync = registration.periodicSync;
}

使用流程如下:

  1. 查看權限
1
2
3
4
5
6
7
8
const status = await navigator.permissions.query({
name: "periodic-background-sync",
});
if (status.state === "granted") {
// 可以使用
} else {
// 需要權限
}
  1. 註冊事件,並給予一個 tag 辨識,底下的例子為 get-latest-news
1
2
3
4
5
6
7
8
9
10
async function registerPeriodicNewsCheck() {
const registration = await navigator.serviceWorker.ready;
try {
await registration.periodicSync.register("get-latest-news", {
minInterval: 24 * 60 * 60 * 1000,
});
} catch {
console.log("不支援");
}
}
  1. 查看是否註冊成功
1
2
3
4
5
6
7
8
navigator.serviceWorker.ready.then((registration) => {
registration.periodicSync.getTags().then((tags) => {
if (tags.includes("get-latest-news")) {
// 成功就跳過某些流程
skipDownloadingLatestNewsOnPageLoad();
}
});
});
  1. 監聽並處理剛才註冊的事件
1
2
3
4
5
6
7
8
9
10
async function updateArticles() {
const articlesCache = await caches.open("articles");
await articlesCache.add("/api/articles");
}

self.addEventListener("periodicsync", (event) => {
if (event.tag == "get-latest-news") {
event.waitUntil(updateArticles());
}
});
  1. 取消註冊的事件
1
2
3
navigator.serviceWorker.ready.then((registration) => {
registration.periodicSync.unregister("get-latest-news");
});

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

share