建立 Service Worker Web Push Notification — (Web Notification 實作紀錄)

Whien
12 min readSep 24, 2017

Service Worker 在現今大型網站中佔了很重要的部分,可以在瀏覽器「不正在」瀏覽當前網頁或正在瀏覽網頁時,將一部分的工作偷偷的丟在背後運作,有在瀏覽其他網頁時,突然接到 youtube 訂閱上傳的通知嗎?記錄一下如何實作(這篇是 Web Notification,下一篇將介紹 Firebase Push Notification 實作紀錄)。

首先利用 express 建立一個網站伺服器

const path = require('path');
const express = require('express');
const app = express();
app.use('/js', express.static(path.resolve(__dirname, '../js')));
app.get('/', (req, res) => {
res.send(`
<html>
<head></head>
<body>
<h1>Push Notification</h1>
<script src='js/main.js'></script>
</body>
</html>
`);
});
app.listen(8998);

建立檔案與資料夾

建立檔案與資料夾

main.js

要啟用 Service Worker 要先判斷是否有支援後,才能進行啟用,寫一段判斷的程式碼

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

得到「Service Worker and Push is supported」確定支援後就可以「註冊」Service Worker 了,再來將註冊的方法寫完

let swRegistration;
if ('serviceWorker' in navigator && 'PushManager' in window) {
console.log('Service Worker and Push is supported');
navigator.serviceWorker
.register('/js/sw.js')
.then(swReg => {
console.log('Service Worker is registered', swReg);
swRegistration = swReg;
})
.catch(err => console.log('Service Worker register error.'));
} else {
console.log('Push is not supported');
}
Service Worker is registered

接下來在開發人員工具中 Application > Service Workers 就能看到我們註冊的結果

Application > Service Worker

要使用 Service Worker 的 Push 前必須要先申請一組 key

https://web-push-codelab.appspot.com/

將 Public Key 記錄下來撰寫到 main.js 中

const applicationServerPublicKey =
'BPa8duHWL0iVFQHCx6CqInD1vZSUgQ0eCKPHmh6y4OFr_BLS6MOBWjig-RriDqZUTpU4KHwLTDlT5Cktqle1YyQ';

在 main.js 開始撰寫 Push Notification 的 UI 相關初始化程式碼並整理一下

const applicationServerPublicKey =
'BPa8duHWL0iVFQHCx6CqInD1vZSUgQ0eCKPHmh6y4OFr_BLS6MOBWjig-RriDqZUTpU4KHwLTDlT5Cktqle1YyQ';
if ('serviceWorker' in navigator && 'PushManager' in window) {
console.log('Service Worker and Push is supported');
navigator.serviceWorker
.register('/js/sw.js')
.then(swReg => {
let swRegistration = swReg;
console.log('Service Worker is registered', swReg);
return Promise.resolve(swRegistration);
})
.then(swRegistration => inititalUI(swRegistration))
.catch(err => console.log('Service Worker register error.'));
} else {
console.log('Push is not supported');
}
function inititalUI(swRegistration) {
swRegistration.pushManager.getSubscription().then(subscription => {
let isSubscription = !(subscription === null);
if (isSubscription) {
console.log('User is subscribed.');
} else {
console.log('User is not subscribed.');
}
});
}
User is not subscribed.

因為使用者還沒有同意授權 Push Notification,所以得到「未訂閱」,接下來要寫一段啟用授權同意給使用者的方法

function urlB64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
function subscribeUser(swRegistration) {
const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
swRegistration.pushManager
.subscribe({
userVisibleOnly: true,
applicationServerKey
})
.then(subscription => {
console.log('User is subscribe.');
})
.catch(err => console.log('Failed to subscribe the user: ', err));
}

並將 main.js 改為這樣

呼叫 subscribeUser

接下來重新整理網頁就會看到詢問是否要啟用推播的結果

點擊同意(Allow)後就會看到 Console 中出現「User is subscribe.」就是訂閱完成

最後要撰寫推送的訊息的方法在 sw.js 中,先寫個簡單的試試看 push 會不會動

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

撰寫完後,回到 Application > Service Worker 點擊 Update 更新 sw.js 檔案後,在點擊 push 就會看到 Console 中秀出結果

再來就是要將畫面推送出來

const SW = '[Service Worker]';
self.addEventListener('push', event => {
console.log(`${SW} Push Received.`);
console.log(`${SW} Push had this data: ${event.data.text()}`);
const title = '推送好消息';
const options = {
body: '好消息報報'
// icon: '',
// badge: ''
};
setTimeout(() => {
event.waitUntil(self.registration.showNotification(title, options));
}, 5000);
});

撰寫完後,回到 Application > Service Worker 點擊 Update 再點擊 Push 就會跑出視窗了!

Service Worker Web Push Notification

如果要點擊後開啟網頁,就要多加一個「notificationclick」的事件

self.addEventListener('notificationclick', event => {
console.log(`${SW} Notification click Received.`);
event.notification.close(); event.waitUntil(
clients.openWindow(
'https://developers.google.com/web/fundamentals/getting- started/codelabs/push-notifications/'
)
);
});

--

--

Whien

遨遊在硬體與軟體世界中,對於計算機一切事物都充滿好奇及熱情。