在網頁應用程式中加入推播通知

1. 總覽

推送訊息是一種簡單又有效的方法,可讓您與使用者再次互動。在本程式碼研究室中,您將瞭解如何在網頁應用程式中加入推播通知。

課程內容

  • 如何為使用者訂閱及取消訂閱推送訊息
  • 如何處理傳入的推送訊息
  • 如何顯示通知
  • 如何回應通知點擊

軟硬體需求

  • Chrome 52 以上版本
  • Chrome 版網路伺服器或您自選的網路伺服器
  • 文字編輯器
  • HTML、CSS、JavaScript 和 Chrome 開發人員工具的基本知識
  • 程式碼範例 (請參閱「進行設定」)。

2. 做好準備

下載程式碼範例

您可以透過兩種方式取得本程式碼研究室的程式碼範例:

  • 複製 Git 存放區:
git clone https://github.com/GoogleChrome/push-notifications.git
  • 下載 ZIP 檔案:

如果將來源下載為 ZIP 檔案,解壓縮後就會取得根資料夾 push-notifications-master

安裝並驗證網路伺服器

雖然您自己的網路伺服器是免費的,但本程式碼研究室適合與 Chrome 版 Web 應用程式搭配使用。如果尚未安裝這個應用程式,可以前往 Chrome 線上應用程式商店下載:

安裝 Chrome 應用程式的網路伺服器後,請按一下書籤列上的「應用程式」捷徑:

946bcaaad66e5c8e.png

在「應用程式」視窗中,按一下「網路伺服器」圖示:

9f3c21b2cf6cbfb5.png

您接下來會看到這個對話方塊,可讓您設定本機網路伺服器:

73543edeb27c3d6f.png

按一下「選取資料夾」按鈕,然後選取下載後 push-notifications 資料夾中的 app 資料夾。這樣一來,您就可以透過對話方塊的「網路伺服器網址」部分顯示的網址,提供處理中的工作。

在「Options」下方,勾選「自動顯示 index.html」旁的方塊,如下所示:

5ac11bca86ce7369.png

接著,將「Web Server: STARTED」(網路伺服器:STARTED) 切換按鈕往左側,再向右移回右側,藉此停止並重新啟動伺服器。

d42f87972f9fec24.png

按一下網路伺服器網址,在網路瀏覽器中造訪您的網站。您應該會看到類似下方的頁面,不過您的版本網址可能會顯示 127.0.0.1:8887:

00-push-codelab.png

一律更新 Service Worker

在開發過程中,建議您確認服務工作處理程序使用的是最新版本並擁有最新變更。

如何在 Chrome 中設定這項功能:

  1. 前往「Push Codelab」分頁。
  2. 開啟開發人員工具:Windows 和 Linux 系統的 Ctrl-Shift-I 鍵;macOS 為 Cmd-Option-I 鍵。
  3. 選取「Application」面板,按一下「Service Workers」分頁標籤,然後勾選「Update on Reload」核取方塊。啟用這個核取方塊後,每次網頁重新載入時,Service Worker 就會強制更新。

e7d384fb77885b99.png

3. 註冊 Service Worker

完成的程式碼

請注意,app 目錄中有一個名為 sw.js 的空白檔案。這個檔案會做為您的 Service Worker。但目前可以留空。稍後您要在其中加入程式碼。

首先,您必須將這個檔案註冊為 Service Worker。

您的 app/index.html 網頁載入 scripts/main.js。您可以在這個 JavaScript 檔案中註冊 Service Worker。

scripts/main.js 加入以下程式碼:

if ('serviceWorker' in navigator && 'PushManager' in window) {
  console.log('Service Worker and Push are supported');

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

    swRegistration = swReg;
  })
  .catch(function(error) {
    console.error('Service Worker Error', error);
  });
} else {
  console.warn('Push messaging is not supported');
  pushButton.textContent = 'Push Not Supported';
}

這個程式碼會檢查您的瀏覽器是否支援 Service Worker 和推送訊息。如果支援這些程式碼,程式碼就會註冊您的 sw.js 檔案。

馬上試試

如要檢查變更,請在瀏覽器中重新整理「Push Codelab」分頁。

在 Chrome 開發人員工具中查看 Service Worker is registered message 的主控台,如下所示:

5d7ad383d6f235d5.png

取得應用程式伺服器金鑰

如要使用本程式碼研究室,您必須先產生應用程式伺服器金鑰。您可以在隨附網站執行這項動作:web-push-codelab.glitch.me

您可以在此處產生公開與私密金鑰組。

push-codelab-04-companion.png

將公開金鑰複製到 scripts/main.js 中,並取代 <Your Public Key> 值:

const applicationServerPublicKey = '<Your Public Key>';

重要事項:切勿將私密金鑰放在網頁應用程式中!

4. 初始化狀態

完成的程式碼

目前網頁應用程式的「啟用」按鈕已停用,因此無法點選。這是因為建議您預設停用「推播」按鈕,等到確定瀏覽器支援推送訊息功能後,再啟用這個按鈕。這樣一來,您就能檢查使用者目前是否已訂閱訊息功能。

您必須在 scripts/main.js 中建立兩個函式:

  • initializeUI,檢查使用者目前是否已訂閱
  • updateBtn,啟用您的按鈕,並根據使用者是否訂閱變更文字

main.js 新增 initializeUI 函式,如下所示:

function initializeUI() {
  // 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();
  });
}

新方法會使用上一個步驟的 swRegistration、從其取得 pushManager 屬性,並對其呼叫 getSubscription()

pushManagergetSubscription() 會傳回承諾,可解決目前訂閱項目的問題 (如有)。如果沒有,則會傳回 null。如此一來,您就可以檢查使用者是否已訂閱、設定 isSubscribed 的值,然後呼叫 updateBtn() 來更新按鈕。

updateBtn() 函式新增至 main.js

function updateBtn() {
  if (isSubscribed) {
    pushButton.textContent = 'Disable Push Messaging';
  } else {
    pushButton.textContent = 'Enable Push Messaging';
  }

  pushButton.disabled = false;
}

這個函式會啟用按鈕,並根據使用者是否訂閱變更按鈕文字。

最後,當 Service Worker 已在 main.js 中註冊時,呼叫 initializeUI()

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

  swRegistration = swReg;
  initializeUI();
})

馬上試試

重新整理「Push Codelab」分頁。您應該會看到「Enable Push Messaging」(啟用推送訊息) 按鈕現在已啟用 (您可以點選該按鈕),接著控制台中應該會顯示 User is NOT subscribed

a1553f4a0483d227.png

在您完成本程式碼研究室的其餘步驟時,只要訂閱或取消訂閱,按鈕文字應該就會出現變化。

5. 訂閱使用者

完成的程式碼

目前 [啟用推送訊息] 按鈕功能沒什麼作用。讓我們一起解決這個問題!

initializeUI() 函式中,新增按鈕的點擊事件監聽器:

function initializeUI() {
  pushButton.addEventListener('click', function() {
    pushButton.disabled = true;
    if (isSubscribed) {
      // TODO: Unsubscribe user
    } else {
      subscribeUser();
    }
  });

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

    updateSubscriptionOnServer(subscription);

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

    updateBtn();
  });
}

當使用者按一下該按鈕時,您只會停用該按鈕,以確保使用者不會再次點選該按鈕,因為訂閱推播訊息可能需要一些時間。

如果使用者目前沒有訂閱,則呼叫 subscribeUser()。為此,您需要將下列程式碼貼入 scripts/main.js

function subscribeUser() {
  const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
  swRegistration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: applicationServerKey
  })
  .then(function(subscription) {
    console.log('User is subscribed.');

    updateSubscriptionOnServer(subscription);

    isSubscribed = true;

    updateBtn();
  })
  .catch(function(error) {
    console.error('Failed to subscribe the user: ', error);
    updateBtn();
  });
}

讓我們逐步瞭解該程式碼的用途,以及如何為使用者訂閱推送訊息。

首先,您要取得應用程式伺服器的公開金鑰 (使用 Base64 網址安全編碼),然後將金鑰轉換為 UInt8Array,因為這是 subscribe() 呼叫預期的輸入內容。urlB64ToUint8Array() 函式位於 scripts/main.js 的頂端。

轉換值之後,請在 Service Worker 的 pushManager 上呼叫 subscribe() 方法,傳入應用程式伺服器的公開金鑰和 userVisibleOnly: true 值。

const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
swRegistration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: applicationServerKey
})

userVisibleOnly 參數可確保每次傳送推送訊息時顯示通知。目前這是必填欄位,必須為 true。

呼叫 subscribe() 會傳回承諾,完成下列步驟後會解決:

  1. 使用者已授予顯示通知的權限。
  2. 瀏覽器已將網路要求傳送至推送服務,以取得產生 PushSubscription 所需的資料。

如果上述步驟成功,subscribe() 承諾將透過 PushSubscription 解決。如果使用者未授予權限,或是訂閱使用者時發生問題,保證會遭到拒絕並傳回錯誤。這可在程式碼研究室中為您提供以下承諾鏈:

swRegistration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: applicationServerKey
})
.then(function(subscription) {
  console.log('User is subscribed.');

  updateSubscriptionOnServer(subscription);

  isSubscribed = true;

  updateBtn();

})
.catch(function(err) {
  console.log('Failed to subscribe the user: ', err);
  updateBtn();
});

此時,您可以取得訂閱項目,並將使用者視為已訂閱,或是擷取錯誤並將其記錄到控制台。在這兩種情況下,您都會呼叫 updateBtn(),確保按鈕已經重新啟用,且含有適當文字。

在實際應用程式中,updateSubscriptionOnServer() 函式可讓您將訂閱資料傳送至後端,但在本程式碼研究室中,您只需在 UI 中顯示訂閱項目即可。在 scripts/main.js 中新增下列函式:

function updateSubscriptionOnServer(subscription) {
  // TODO: Send subscription to application server

  const subscriptionJson = document.querySelector('.js-subscription-json');
  const subscriptionDetails =
    document.querySelector('.js-subscription-details');

  if (subscription) {
    subscriptionJson.textContent = JSON.stringify(subscription);
    subscriptionDetails.classList.remove('is-invisible');
  } else {
    subscriptionDetails.classList.add('is-invisible');
  }
}

馬上試試

前往「Push Codelab」分頁,重新整理頁面,然後按一下按鈕。畫面應會顯示如下的權限提示:

fcc61267a0194e81.png

如果您授予權限,應該會在控制台中看到 User is subscribed。按鈕文字會變更為「停用推送訊息」,然後您就能在頁面底部以 JSON 資料形式查看訂閱項目。

5c5505f2ead037c.png

6. 處理權限遭拒

完成的程式碼

如果使用者封鎖了權限要求,系統會發生尚未處理的情況。這需要特別考量,因為如果使用者封鎖權限,網頁應用程式就無法重新顯示權限提示,也無法訂閱該名使用者。您至少要停用「推送」按鈕,讓使用者知道該按鈕無法使用。

針對這種情況,最明顯的位置位於 updateBtn() 函式中。您只需檢查 Notification.permission 值,如下所示:

function updateBtn() {
  if (Notification.permission === 'denied') {
    pushButton.textContent = 'Push Messaging Blocked';
    pushButton.disabled = true;
    updateSubscriptionOnServer(null);
    return;
  }

  if (isSubscribed) {
    pushButton.textContent = 'Disable Push Messaging';
  } else {
    pushButton.textContent = 'Enable Push Messaging';
  }

  pushButton.disabled = false;
}

您知道如果此權限為 denied,則使用者無法訂閱,且您也無法進行其他操作,因此最好的做法是永久停用該按鈕。

馬上試試

由於您已在上一個步驟授予網頁應用程式權限,因此需要按一下網址列中的圓圈中的「i」,然後將「通知」權限變更為「使用全域預設值 (要求)」

54495592074f10ae.png

變更這項設定後,請重新整理頁面並按一下「啟用推送訊息」按鈕,然後在權限對話方塊中選取「封鎖」。這個按鈕將會停用,並顯示「Push Messaging Block」(推送訊息已封鎖) 文字。

d4cf22468f6defda.png

透過這項變更,您現在可以為使用者訂閱,以處理可能的權限情境。

7. 處理推送事件

完成的程式碼

在瞭解如何從後端傳送推送訊息之前,需要先考量訂閱的使用者收到推送訊息時會發生什麼情況。

當您觸發推送訊息時,瀏覽器會收到推送訊息、判斷要推送的目標 Service Worker、喚醒 Service Worker,並調度推送事件。您需要監聽這個事件,並在結果中顯示通知。

sw.js 檔案中新增下列程式碼:

self.addEventListener('push', function(event) {
  console.log('[Service Worker] Push Received.');
  console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);

  const title = 'Push Codelab';
  const options = {
    body: 'Yay it works.',
    icon: 'images/icon.png',
    badge: 'images/badge.png'
  };

  event.waitUntil(self.registration.showNotification(title, options));
});

現在來逐步瞭解這個程式碼。您正在透過新增事件監聽器來監聽 Service Worker 中的 push 事件:

self.addEventListener('push', ... );

(除非您先前玩過 Web Worker,否則 self 可能不是最新版本。在 Service Worker 檔案中,self 參照 Service Worker 本身。

收到推送訊息時,系統會呼叫事件監聽器,而您可以在 Service Worker 的 registration 屬性上呼叫 showNotification(),藉此建立通知。showNotification() 需要 title;也可以提供 options 物件,用來設定內文訊息、圖示和標記。(請注意,徽章僅於本文撰寫期間用於 Android 系統)。

const title = 'Push Codelab';
const options = {
  body: 'Yay it works.',
  icon: 'images/icon.png',
  badge: 'images/badge.png'
};
self.registration.showNotification(title, options);

push 事件處理的最後要點是 event.waitUntil()。這個方法需要承諾,可讓瀏覽器保持服務工作處理程序的持續狀態,直到傳遞到 的承諾後,才持續運作。

為了讓上方的程式碼更容易理解,您可以像這樣重新編寫:

const notificationPromise = self.registration.showNotification(title, options);
event.waitUntil(notificationPromise);

現在您已經完成推送事件,讓我們開始測試推送事件。

馬上試試

在 Service Worker 中,您可以透過推送事件處理功能觸發假推送事件,測試收到訊息時會發生什麼事。

在網頁應用程式中,訂閱推送訊息服務,並確認控制台中會顯示 User IS subscribed。在開發人員工具的「Application」面板中,點選「Service Workers」分頁標籤下方的「Push」按鈕:

1ee499267eeccd1c.png

點選「推送」後,您應該會看到類似下方的通知:

379105dfb0ea56d8.png

注意:如果這個步驟無法解決問題,請嘗試使用開發人員工具應用程式面板中的 [取消註冊] 連結取消註冊 Service Worker,並等待 Service Worker 停止,再重新載入頁面。

8. 通知點擊

完成的程式碼

如果您點選其中一則通知,您會發現沒有任何變化。您可以監聽 Service Worker 中的 notificationclick 事件,處理通知點擊。

請先在 sw.js 中新增 notificationclick 事件監聽器:

self.addEventListener('notificationclick', function(event) {
  console.log('[Service Worker] Notification click received.');

  event.notification.close();

  event.waitUntil(
    clients.openWindow('https://developers.google.com/web')
  );
});

使用者點選通知時,系統會呼叫 notificationclick 事件監聽器。

程式碼會先關閉使用者點選的通知:

event.notification.close();

接著開啟新視窗或分頁並載入網址 https://developers.google.com/web。您可以變更這項設定。

event.waitUntil(
    clients.openWindow('https://developers.google.com/web/')
  );

event.waitUntil() 確保在顯示新視窗或分頁之前,瀏覽器不會終止 Service Worker。

馬上試試

請嘗試在開發人員工具中再次觸發推送訊息,然後按一下通知。這時通知會關閉,並且開啟新分頁。

9. 傳送推送訊息

您已經發現網頁應用程式可以使用開發人員工具顯示通知,並研究如何按一下滑鼠來關閉通知。下一步是傳送實際的推送訊息。

一般而言,使用者必須將訂閱項目從網頁傳送至後端。接著,後端會對訂閱項目中的端點發出 API 呼叫,藉此觸發推送訊息。

此內容不在本程式碼研究室的涵蓋範圍內,但您可以使用隨附網站 ( web-push-codelab.glitch.me) 觸發實際的推送訊息。將訂閱項目貼到頁面底部:

bb202867962a0249.png

然後貼到 [Subscription to Send to] (訂閱對象) 文字區域的隨附網站中:

a0dd93bc33a9e8cf.png

在「文字要傳送」下方,新增要隨推送訊息傳送的任何字串。

按一下 [傳送推送訊息] 按鈕。

a5e8e89411ec034.png

你應該會收到推送訊息。您使用的文字會記錄在控制台中。

f6815a356d4f9aaa.png

您應該可以藉此測試傳送和接收資料,並進而操控通知。

隨附應用程式只是節點伺服器,會使用網頁推送程式庫傳送訊息。建議您查看 GitHub 上的 web-push-libs 機構,瞭解有哪些程式庫可用於傳送推送訊息。這個動作會處理許多細節,觸發推送訊息。

您可以查看隨附網站的所有程式碼

10. 取消訂閱使用者

完成的程式碼

還有一個好處是能夠取消訂閱使用者。為此,您必須在 PushSubscription 上呼叫 unsubscribe()

返回 scripts/main.js 檔案,將 initializeUI() 中的 pushButton 點擊事件監聽器變更為以下內容:

pushButton.addEventListener('click', function() {
  pushButton.disabled = true;
  if (isSubscribed) {
    unsubscribeUser();
  } else {
    subscribeUser();
  }
});

請注意,您現在將呼叫新的函式 unsubscribeUser()。在這個函式中,您可以取得目前的訂閱項目,並對其呼叫 unsubscribe()。在 scripts/main.js 加入以下程式碼:

function unsubscribeUser() {
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    if (subscription) {
      return subscription.unsubscribe();
    }
  })
  .catch(function(error) {
    console.log('Error unsubscribing', error);
  })
  .then(function() {
    updateSubscriptionOnServer(null);

    console.log('User is unsubscribed.');
    isSubscribed = false;

    updateBtn();
  });
}

讓我們逐步瞭解這個函式。

首先,請呼叫 getSubscription() 來取得目前的訂閱項目:

swRegistration.pushManager.getSubscription()

這會傳回以 PushSubscription 解決的承諾 (如果有的話);如果沒有,則會傳回 null。如果有訂閱項目,請對其呼叫 unsubscribe(),這會使 PushSubscription 無效。

swRegistration.pushManager.getSubscription()
.then(function(subscription) {
  if (subscription) {
    // TODO: Tell application server to delete subscription
    return subscription.unsubscribe();
  }
})
.catch(function(error) {
  console.log('Error unsubscribing', error);
})

呼叫 unsubscribe() 會傳回承諾,因為可能需要一段時間才能完成。您傳回該承諾,因此鏈結中的下一個 then() 會等待 unsubscribe() 完成。也可以新增擷取處理常式,以防呼叫 unsubscribe() 導致錯誤。這樣就能更新使用者介面。

.then(function() {
  updateSubscriptionOnServer(null);

  console.log('User is unsubscribed.');
  isSubscribed = false;

  updateBtn();
})

馬上試試

您應可以在網頁應用程式中按下 [啟用推送訊息功能] 或 [停用推送訊息],這樣就能在記錄中看到使用者已經訂閱和取消訂閱了。

81a07119235b53da.png

11. 已完成

恭喜您完成本程式碼研究室!

本程式碼研究室已說明如何在網頁應用程式中新增推播通知,快速上手。如要進一步瞭解網頁通知的功能,請參閱這些文件

如果您想在網站上部署推播通知,建議您支援舊版瀏覽器,或使用 GCM 的非標準瀏覽器更新。請按這裡瞭解詳情。

其他資訊

相關網誌文章