程式碼研究室:建構推播通知用戶端

Kate Jeffreys
Kate Jeffreys
Kayce Basques
Kayce Basques

本程式碼實驗室會逐步說明如何建構推播通知用戶端。完成程式碼實驗室後,您將擁有一個用戶端,可執行以下操作:

  • 為使用者訂閱推播通知。
  • 接收推播訊息並顯示為通知。
  • 取消訂閱使用者的推播通知。

本程式碼實驗室著重於透過實作學習,不會過多探討概念。請參閱「推播通知的運作方式」,瞭解推播通知的概念。

本程式碼研究室的伺服器程式碼已完成。在本程式碼實驗室中,您只會實作用戶端。如要瞭解如何實作推播通知伺服器,請參閱「程式碼實驗室:建構推播通知伺服器」。

瀏覽器相容性

本程式碼實驗室適用於下列作業系統和瀏覽器組合:

  • Windows:Chrome、Edge
  • macOS:Chrome、Firefox
  • Android:Chrome、Firefox

已知本程式碼實驗室不支援下列作業系統 (或作業系統和瀏覽器組合):

  • macOS:Brave、Edge、Safari
  • iOS

設定

設定驗證方法

您必須先使用驗證金鑰設定伺服器和用戶端,才能使用推播通知。如要瞭解原因,請參閱「簽署網路推播通訊協定要求」。一般來說,您會將 Secret 儲存在 .env 檔案中,類似這樣。

VAPID_PUBLIC_KEY="BKiwTvD9HA…"
VAPID_PRIVATE_KEY="4mXG9jBUaU…"
VAPID_SUBJECT="mailto:test@test.test"
  1. 開啟 public/index.js。
  2. 將 VAPID_PUBLIC_KEY_VALUE_HERE 替換為公開金鑰的值。

註冊 Service Worker

用戶端需要 Service Worker 才能接收及顯示通知。建議您盡早註冊 Service Worker。詳情請參閱「以通知形式接收及顯示推送訊息」。

  1. 使用下列程式碼取代 // TODO add startup logic here 註解:

    if ('serviceWorker' in navigator && 'PushManager' in window) {
      navigator.serviceWorker.register('./service-worker.js').then(serviceWorkerRegistration => {
        console.info('Service worker was registered.');
        console.info({serviceWorkerRegistration});
      }).catch(error => {
        console.error('An error occurred while registering the service worker.');
        console.error(error);
      });
      subscribeButton.disabled = false;
    } else {
      console.error('Browser does not support service workers or push messages.');
    }
    
    subscribeButton.addEventListener('click', subscribeButtonHandler);
    unsubscribeButton.addEventListener('click', unsubscribeButtonHandler);
    
  2. 在 Chrome 中開啟開發人員工具控制台。

  3. 您應該會看到控制台記錄了 Service worker was registered. 訊息。

要求推播通知權限

請勿在載入網頁時要求傳送推播通知的權限。 而是應該在 UI 中詢問使用者是否要接收推播通知。使用者明確確認後 (例如點選按鈕),您就可以開始正式程序,向瀏覽器取得推播通知權限。

  1. 在 public/index.js 中,將 subscribeButtonHandler() 中的 // TODO 註解替換為以下程式碼:

    // Prevent the user from clicking the subscribe button multiple times.
    subscribeButton.disabled = true;
    const result = await Notification.requestPermission();
    if (result === 'denied') {
      console.error('The user explicitly denied the permission request.');
      return;
    }
    if (result === 'granted') {
      console.info('The user accepted the permission request.');
    }
    
  2. 返回應用程式分頁,然後點選「Subscribe to push」。瀏覽器或作業系統會詢問你是否要允許網站傳送推播通知。

  3. 選取「Allow」(允許) (或瀏覽器/作業系統使用的對應詞組)。控制台會顯示訊息,指出要求是否獲得核准。

訂閱推播通知

訂閱程序涉及與瀏覽器供應商控制的網路服務互動,稱為推播服務。取得推播通知訂閱資訊後,您需要將資訊傳送至伺服器,並讓伺服器將資訊長期儲存在資料庫中。

  1. 在 subscribeButtonHandler() 中加入下列醒目顯示的程式碼:

    subscribeButton.disabled = true;
    const result = await Notification.requestPermission();
    if (result === 'denied') {
      console.error('The user explicitly denied the permission request.');
      return;
    }
    if (result === 'granted') {
      console.info('The user accepted the permission request.');
    }
    const registration = await navigator.serviceWorker.getRegistration();
    const subscribed = await registration.pushManager.getSubscription();
    if (subscribed) {
      console.info('User is already subscribed.');
      notifyMeButton.disabled = false;
      unsubscribeButton.disabled = false;
      return;
    }
    const subscription = await registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: urlB64ToUint8Array(VAPID_PUBLIC_KEY)
    });
    notifyMeButton.disabled = false;
    fetch('/add-subscription', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(subscription)
    });
    

userVisibleOnly 選項必須是 true。未來或許可以推送訊息,而不顯示使用者可見的通知 (靜音推送),但基於隱私權疑慮,瀏覽器不允許這項功能。

applicationServerKey 值會依賴公用函式,將 Base64 字串轉換為 Uint8Array。這個值用於伺服器和推播服務之間的驗證。

取消訂閱推播通知

使用者訂閱推播通知後,您的 UI 必須提供取消訂閱的方法,以免使用者改變心意,不想再接收推播通知。

  • 將 unsubscribeButtonHandler() 中的 // TODO 註解替換為以下程式碼:
const registration = await navigator.serviceWorker.getRegistration();
const subscription = await registration.pushManager.getSubscription();
fetch('/remove-subscription', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({endpoint: subscription.endpoint})
});
const unsubscribed = await subscription.unsubscribe();
if (unsubscribed) {
  console.info('Successfully unsubscribed from push notifications.');
  unsubscribeButton.disabled = true;
  subscribeButton.disabled = false;
  notifyMeButton.disabled = true;
}

接收推播訊息並顯示為通知

如前所述,您需要 Service Worker 來處理從伺服器推送至用戶端的訊息,並顯示這些訊息。詳情請參閱「以通知形式接收及顯示推送訊息」。

  1. 開啟 public/service-worker.js,並將 Service Worker 的 push 事件處理常式中的 // TODO 註解替換為下列程式碼:

    let data = event.data.json();
    const image = 'logo.png';
    const options = {
      body: data.options.body,
      icon: image
    }
    self.registration.showNotification(
      data.title,
      options
    );
    
  2. 返回應用程式分頁。

  3. 按一下「通知我」。您應該會收到推播通知。

  4. 在其他支援的瀏覽器中開啟應用程式分頁的網址。完成訂閱工作流程,然後按一下「Notify all」(全部通知)。您應該會在每個裝置上收到相同的推播通知。

您可以透過多種方式自訂通知。詳情請參閱 ServiceWorkerRegistration.showNotification() 的參數。

在使用者點選通知時開啟網址

在實際情況中,您可能會使用通知來重新吸引使用者,並提示他們造訪您的網站。如要這麼做,您需要進一步設定 Service Worker。

  1. 將 Service Worker 的 notificationclick 事件處理常式中的 // TODO 註解替換為下列程式碼:

    event.notification.close();
    event.waitUntil(self.clients.openWindow('https://web.dev'));
    
  2. 返回應用程式分頁,再傳送一則通知給自己,然後按一下通知。瀏覽器應會開啟新分頁並載入 https://web.dev。

後續步驟