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

Kate Jeffreys
Kate Jeffreys
Kayce Basques
Kayce Basques

本程式碼研究室會逐步說明如何建構推播通知用戶端。完成本程式碼研究室後,您將擁有下列用戶端:

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

本程式碼研究室著重於透過實作來學習,不會深入探討概念。如要瞭解推播通知的概念,請參閱「推播通知的運作方式為何?」。

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

瀏覽器相容性

據瞭解,本程式碼研究室適用於下列作業系統和瀏覽器組合:

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

已知本程式碼研究室無法在下列作業系統 (或作業系統和瀏覽器組合) 中運作:

  • macOS:Brave、Edge、Safari
  • iOS

設定

取得可編輯的程式碼副本

  • 按一下「Remix to Edit」,即可編輯專案。

設定驗證方法

如要讓推播通知正常運作,請先使用驗證金鑰設定伺服器和用戶端。如要瞭解原因,請參閱「簽署網頁推送通訊協定要求」。通常你會將密鑰儲存在類似這樣的 .env 檔案中。

VAPID_PUBLIC_KEY="BKiwTvD9HA…" VAPID_PRIVATE_KEY="4mXG9jBUaU…" VAPID_SUBJECT="mailto:[email protected]" 
  • 開啟 public/index.js
  • VAPID_PUBLIC_KEY_VALUE_HERE 替換為公開金鑰的值。

註冊 Service Worker

最終,您的用戶端需要服務工作人員才能接收及顯示通知。建議盡早註冊 Service Worker。 如需更多背景資訊,請參閱「以通知形式接收及顯示推送的訊息」。

  • // TODO add startup logic here 註解替換為下列程式碼:
// 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); 
  • 按下 `Control+Shift+J` 鍵 (在 Mac 上為 `Command+Option+J` 鍵) 開啟開發人員工具。
  • 再按一下 [Console] (控制台) 標籤即可。您應該會在控制台中看到記錄的訊息 Service worker was registered.

要求推播通知權限

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

  • public/index.js 中,將 subscribeButtonHandler() 中的 // TODO 註解替換為以下程式碼:
// 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.'); } 
  • 返回應用程式分頁,然後按一下「Subscribe to push」(訂閱推播)。瀏覽器或作業系統可能會詢問您是否要允許網站傳送推播通知。按一下「允許」 (或瀏覽器/作業系統使用的同義詞)。控制台會顯示訊息,指出要求是否獲得核准。

訂閱推播通知

訂閱程序會與瀏覽器廠商控管的網路服務 (稱為「推送服務」) 互動。取得推送通知訂閱資訊後,請將資訊傳送至伺服器,並讓伺服器長期儲存在資料庫中。如要進一步瞭解訂閱程序,請參閱「為用戶端訂閱推播通知」。

  • 將下列醒目顯示的程式碼新增至 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 註解替換為下列程式碼:
// 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; } 

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

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

  • 開啟 public/service-worker.js,並將服務工作人員 push 事件處理常式中的 // TODO 註解替換為下列程式碼:
// TODO let data = event.data.json(); const image = 'logo.png'; const options = {   body: data.options.body,   icon: image } self.registration.showNotification(   data.title,    options ); 
  • 返回應用程式分頁。
  • 按一下「通知我」。你應該會收到推播通知。
  • 請嘗試在其他瀏覽器 (或甚至其他裝置) 開啟應用程式分頁的網址,完成訂閱流程,然後按一下「通知所有人」。你訂閱的所有瀏覽器都應收到相同的推播通知。請參閱「瀏覽器相容性」一節,查看已知可運作或無法運作的瀏覽器/作業系統組合清單。

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

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

在現實世界中,您可能會使用通知重新吸引使用者,並提示他們造訪您的網站。如要這麼做,您需要進一步設定服務工作人員。

  • 將服務工作人員 notificationclick 事件處理常式中的 // TODO 註解替換為下列程式碼:
// TODO event.notification.close(); event.waitUntil(self.clients.openWindow('https://web.dev')); 
  • 返回應用程式分頁,再次傳送通知給自己,然後按一下通知。瀏覽器應會開啟新分頁並載入 https://web.dev

後續步驟