使用 Service Worker 管理通知

Kate Jeffreys
Kate Jeffreys

在本程式碼研究室中,您將使用 Service Worker 管理通知。本頁的說明假設您已熟悉 Service Worker,以及要求通知權限和傳送通知的基本知識。如需複習通知相關知識,請參閱「開始使用 Notifications API」程式碼研究室。如要進一步瞭解 Service Worker,請參閱 Matt Gaunt 的 Service Worker 簡介

熟悉範例應用程式和範例程式碼

首先,請在新 Chrome 分頁中查看即時應用程式:

  1. 按下 `Control+Shift+J` 鍵 (在 Mac 上為 `Command+Option+J` 鍵) 開啟開發人員工具。
  2. 再按一下「Console」(控制台) 分頁標籤即可。

  3. 確認「篩選器」方塊旁的「層級」下拉式選單中已選取「資訊」選項

  4. 在即時應用程式的開發人員工具控制台中,您應該會看到控制台訊息:

    TODO: Implement getRegistration()

    這是函式存根傳送的訊息,您將在本程式碼研究室中實作該函式。

現在來看看範例應用程式的程式碼。

  1. 請參閱public/index.js

    • 您要實作的函式有四個存根:registerServiceWorkergetRegistrationunRegisterServiceWorkersendNotification

    • requestPermission 函式會要求使用者授予傳送通知的權限。如果您已完成「開始使用 Notifications API」程式碼研究室,就會發現這裡使用了 requestPermission 函式。唯一的差別在於,現在系統也會在解決權限要求後更新使用者介面。

    • updateUI 函式會重新整理應用程式的所有按鈕和訊息。

    • initializePage 函式會偵測瀏覽器中的 Service Worker 功能,並更新應用程式使用者介面。

    • 指令碼會等到頁面載入完成後再初始化。

  2. 開啟 public/service-worker.js

    顧名思義,您要將程式碼新增至應用程式,將這個檔案註冊為服務工作人員

    雖然應用程式尚未使用這個檔案,但其中包含一些啟動程式碼,會在 Service Worker 啟動時將訊息列印到控制台。

    您會在 public/service-worker.js 中新增程式碼,以便在服務工作人員收到通知時處理通知。

註冊 Service Worker

在這個步驟中,您將編寫程式碼,在使用者點選應用程式 UI 中的「Register service worker」(註冊 Service Worker) 時執行。這段程式碼會將 public/service-worker.js 註冊為 Service Worker。

  1. 開啟 public/index.js。將 registerServiceWorker 函式替換為下列程式碼:

    // Use the Service Worker API to register a service worker.
    async function registerServiceWorker() {
      await navigator.serviceWorker.register('./service-worker.js')
      updateUI();
    }
    

    請注意,registerServiceWorker 使用 async function 宣告,讓處理 Promise 更方便。這可讓您await Promise 的已解析值。舉例來說,上述函式會等待註冊服務工作站的結果,再更新 UI。詳情請參閱 MDN 上的這篇文章await

  2. 使用者現在可以註冊 Service Worker,您可以取得 Service Worker 註冊物件的參照。在 public/index.js 中,將 getRegistration 函式替換為下列程式碼:

    // Get the current service worker registration.
    function getRegistration() {
      return navigator.serviceWorker.getRegistration();
    }
    

    上述函式會使用 Service Worker API 取得目前的 Service Worker 註冊 (如有)。這樣一來,取得服務工作站註冊的參照會更方便。

  • 如要完成 Service Worker 註冊功能,請新增程式碼來取消註冊 Service Worker。將 unRegisterServiceWorker 函式替換為下列程式碼:

    // Unregister a service worker, then update the UI.
    async function unRegisterServiceWorker() {
      // Get a reference to the service worker registration.
      let registration = await getRegistration();
      // Await the outcome of the unregistration attempt
      // so that the UI update is not superceded by a
      // returning Promise.
      await registration.unregister();
      updateUI();
    }
    

在顯示即時應用程式的分頁中,重新載入頁面。「註冊 Service Worker」和「取消註冊 Service Worker」按鈕現在應該可以正常運作。

將通知傳送給 Service Worker

在這個步驟中,您將編寫程式碼,在使用者點選應用程式 UI 中的「傳送通知」時執行。這段程式碼會建立通知、檢查是否已註冊 Service Worker,然後使用 postMessage 方法將通知傳送至 Service Worker。

開啟 public/index.js,並將 sendNotification 函式替換為下列程式碼:

// Create and send a test notification to the service worker.
async function sendNotification() {
  // Use a random number as part of the notification data
  // (so you can tell the notifications apart during testing!)
  let randy = Math.floor(Math.random() * 100);
  let notification = {
    title: 'Test ' + randy,
    options: { body: 'Test body ' + randy }
  };
  // Get a reference to the service worker registration.
  let registration = await getRegistration();
  // Check that the service worker registration exists.
  if (registration) {
    // Check that a service worker controller exists before
    // trying to access the postMessage method.
    if (navigator.serviceWorker.controller) {
      navigator.serviceWorker.controller.postMessage(notification);
    } else {
      console.log('No service worker controller found. Try a soft reload.');
    }
  }
}

這段程式碼的用途如下:

  • sendNotification 是非同步函式,因此您可以使用 await 取得服務工作人員註冊的參照。

  • 服務工作站的 postMessage 方法會將資料從應用程式傳送至服務工作站。詳情請參閱 MDN 說明文件中的 postMessage

  • 程式碼會先檢查 navigator.serviceWorker.controller 屬性是否存在,再嘗試存取 postMessage 函式。如果沒有作用中的 Service Worker,或網頁已強制重新整理 (Shift+重新載入),navigator.serviceWorker.controller 會是 null。詳情請參閱 MDN 上的 ServiceWorker 控制器說明文件

在 Service Worker 中處理通知

在這個步驟中,您會在 Service Worker 中編寫程式碼,處理發布至該程式碼的訊息,並向使用者顯示通知。

開啟 public/service-worker.js。在檔案結尾新增下列程式碼:

// Show notification when received
self.addEventListener('message', (event) => {
  let notification = event.data;
  self.registration.showNotification(
    notification.title,
    notification.options
  ).catch((error) => {
    console.log(error);
  });
});

簡單說明如下:

  • self 是指服務工作人員本身。

  • 雖然服務工作人員現在負責顯示通知,但主要應用程式 UI 仍須負責取得使用者的通知權限。如果未授予權限,showNotification 傳回的 Promise 會遭到拒絕。上述程式碼使用 catch 區塊,避免未捕捉到的 Promise 拒絕錯誤,並以更優雅的方式處理這項錯誤。

請繼續參加本系列的下一個程式碼研究室:建構推播通知伺服器