เราจะมาดูรูปแบบการใช้งานทั่วไปบางส่วนสำหรับพุชจากเว็บ
การดำเนินการนี้จะเกี่ยวข้องกับการใช้ API ต่างๆ 2-3 รายการที่มีให้ใช้งานในโปรแกรมทำงานของบริการ
เหตุการณ์การปิดการแจ้งเตือน
ในส่วนสุดท้าย เราได้เห็นวิธีที่เราสามารถฟังเหตุการณ์ notificationclick
นอกจากนี้ยังมีเหตุการณ์ notificationclose
ที่เรียกใช้หากผู้ใช้ปิดการแจ้งเตือนรายการใดรายการหนึ่งของคุณ (กล่าวคือ ผู้ใช้คลิกกากบาทหรือเลื่อนการแจ้งเตือนออกไปแทนการคลิกการแจ้งเตือน)
โดยปกติแล้ว เหตุการณ์นี้จะใช้สำหรับข้อมูลวิเคราะห์เพื่อติดตามการมีส่วนร่วมของผู้ใช้กับการแจ้งเตือน
self.addEventListener('notificationclose', function (event) {
const dismissedNotification = event.notification;
const promiseChain = notificationCloseAnalytics();
event.waitUntil(promiseChain);
});
การเพิ่มข้อมูลในการแจ้งเตือน
เมื่อได้รับข้อความ Push เป็นเรื่องปกติที่จะมีข้อมูลที่เป็นประโยชน์เฉพาะในกรณีที่ผู้ใช้คลิกการแจ้งเตือน เช่น URL ที่ควรเปิดเมื่อมีการคลิกการแจ้งเตือน
วิธีที่ง่ายที่สุดในการนำข้อมูลจากเหตุการณ์พุชมาแนบไปกับการแจ้งเตือนคือการเพิ่มพารามิเตอร์ data
ลงในออบเจ็กต์ตัวเลือกที่ส่งไปยัง showNotification()
ดังตัวอย่างต่อไปนี้
const options = {
body:
'This notification has data attached to it that is printed ' +
"to the console when it's clicked.",
tag: 'data-notification',
data: {
time: new Date(Date.now()).toString(),
message: 'Hello, World!',
},
};
registration.showNotification('Notification with Data', options);
ภายในเครื่องจัดการคลิก ระบบจะเข้าถึงข้อมูลได้ด้วย event.notification.data
const notificationData = event.notification.data;
console.log('');
console.log('The notification data has the following parameters:');
Object.keys(notificationData).forEach((key) => {
console.log(` ${key}: ${notificationData[key]}`);
});
console.log('');
เปิดหน้าต่าง
หนึ่งในการตอบสนองที่พบบ่อยที่สุดต่อการแจ้งเตือนคือการเปิดหน้าต่าง / แท็บไปยัง URL ที่ต้องการ ซึ่งทำได้โดยใช้ clients.openWindow()
API
ในเหตุการณ์ notificationclick
เราจะเรียกใช้โค้ดดังนี้
const examplePage = '/demos/notification-examples/example-page.html';
const promiseChain = clients.openWindow(examplePage);
event.waitUntil(promiseChain);
ในส่วนถัดไป เราจะมาดูวิธีตรวจสอบว่าหน้าเว็บที่เราต้องการนำผู้ใช้ไปเปิดแล้วหรือยัง วิธีนี้จะช่วยให้เราสามารถโฟกัสแท็บที่เปิดอยู่แทนการเปิดแท็บใหม่
โฟกัสหน้าต่างที่มีอยู่
เมื่อเป็นไปได้ เราควรโฟกัสที่หน้าต่างแทนที่จะเปิดหน้าต่างใหม่ทุกครั้งที่ผู้ใช้คลิกการแจ้งเตือน
ก่อนที่เราจะดูวิธีบรรลุเป้าหมายนี้ คุณควรเน้นย้ำให้ทราบว่าวิธีนี้จะเป็นไปได้สำหรับหน้าเว็บในต้นทางเท่านั้น นั่นเป็นเพราะเราเห็นเฉพาะหน้าที่เปิดอยู่ซึ่งเป็นของเว็บไซต์ของเรา เพื่อป้องกันไม่ให้นักพัฒนาซอฟต์แวร์สามารถดูเว็บไซต์ทั้งหมดที่ผู้ใช้กำลังดูอยู่
ตามตัวอย่างก่อนหน้านี้ เราจะแก้ไขโค้ดเพื่อดูว่า /demos/notification-examples/example-page.html
เปิดอยู่แล้วหรือไม่
const urlToOpen = new URL(examplePage, self.location.origin).href;
const promiseChain = clients
.matchAll({
type: 'window',
includeUncontrolled: true,
})
.then((windowClients) => {
let matchingClient = null;
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
if (windowClient.url === urlToOpen) {
matchingClient = windowClient;
break;
}
}
if (matchingClient) {
return matchingClient.focus();
} else {
return clients.openWindow(urlToOpen);
}
});
event.waitUntil(promiseChain);
มาดูโค้ดกัน
ก่อนอื่น เราแยกวิเคราะห์หน้าเว็บตัวอย่างของเราโดยใช้ URL API นี่เป็นเคล็ดลับเด็ดๆ ที่ผมมาจาก Jeff Posnick การเรียก new URL()
ด้วยออบเจ็กต์ location
จะแสดงผล URL ที่สมบูรณ์ หากสตริงที่ส่งผ่านเป็นแบบสัมพัทธ์ (นั่นคือ /
จะกลายเป็น https://example.com/
)
เราทำให้ URL เป็นแบบสัมบูรณ์เพื่อให้สามารถจับคู่กับ URL หน้าต่างได้ในภายหลัง
const urlToOpen = new URL(examplePage, self.location.origin).href;
จากนั้นเราจะได้รับรายการออบเจ็กต์ WindowClient
ซึ่งเป็นรายการแท็บและหน้าต่างที่เปิดอยู่ในขณะนี้ (โปรดทราบว่าแท็บเหล่านี้มีไว้สำหรับต้นทางของคุณเท่านั้น)
const promiseChain = clients.matchAll({
type: 'window',
includeUncontrolled: true,
});
ตัวเลือกที่ส่งผ่านไปยัง matchAll
จะแจ้งให้เบราว์เซอร์ทราบว่าเราต้องการค้นหาเฉพาะไคลเอ็นต์ประเภท "window" เท่านั้น (กล่าวคือ ค้นหาแท็บและหน้าต่างเท่านั้น และยกเว้น Web Worker) includeUncontrolled
ช่วยให้เราค้นหาแท็บทั้งหมดจากต้นทางของคุณที่ไม่ได้ควบคุมโดย Service Worker ปัจจุบันได้ ซึ่งก็คือ Service Worker ที่เรียกใช้โค้ดนี้ โดยปกติแล้ว คุณจะต้องระบุ includeUncontrolled
เป็นค่าจริงเสมอเมื่อเรียกใช้ matchAll()
เรายึดคำสัญญาที่ให้ไว้เป็น promiseChain
เพื่อส่งผ่านต่อไปยัง event.waitUntil()
ในอนาคต และทำให้ผู้ปฏิบัติงานบริการของเรารักษาชีวิตต่อไปได้
เมื่อสัญญา matchAll()
คงที่แล้ว เราจะทำซ้ำผ่านไคลเอ็นต์กรอบเวลาที่ส่งคืน และเปรียบเทียบ URL เหล่านั้นกับ URL ที่ต้องการเปิด หากพบรายการที่ตรงกัน เราจะมุ่งความสนใจ
ไปที่ลูกค้า ซึ่งจะทำให้ผู้ใช้สนใจหน้าต่างนั้น โฟกัสเสร็จสิ้นแล้วในการโทร matchingClient.focus()
หากไม่พบลูกค้าที่ตรงกัน เราจะเปิดหน้าต่างใหม่เหมือนในส่วนก่อนหน้านี้
.then((windowClients) => {
let matchingClient = null;
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
if (windowClient.url === urlToOpen) {
matchingClient = windowClient;
break;
}
}
if (matchingClient) {
return matchingClient.focus();
} else {
return clients.openWindow(urlToOpen);
}
});
กำลังรวมการแจ้งเตือน
เราเห็นว่าการเพิ่มแท็กในการแจ้งเตือนเป็นการเลือกให้ระบบแทนที่การแจ้งเตือนที่มีอยู่ซึ่งมีแท็กเดียวกัน
อย่างไรก็ตาม คุณอาจมีความซับซ้อนมากขึ้นด้วยการยุบการแจ้งเตือนโดยใช้ Notifications API ลองใช้แอปแชท ซึ่งนักพัฒนาซอฟต์แวร์อาจต้องการการแจ้งเตือนใหม่ที่จะแสดงข้อความที่คล้ายกับ "คุณมี 2 ข้อความจาก Matt" แทนที่จะแสดงข้อความล่าสุดเท่านั้น
คุณสามารถทำได้หรือจัดการกับการแจ้งเตือนปัจจุบันในลักษณะอื่นๆ โดยใช้ registration.getNotifications() API ที่ให้คุณเข้าถึงการแจ้งเตือนทั้งหมดที่มองเห็นในขณะนี้สำหรับเว็บแอป
มาดูกันว่าเราจะนำ API นี้ไปใช้ในตัวอย่างแชทได้อย่างไร
ในแอปแชท เราจะสมมติว่าการแจ้งเตือนแต่ละรายการมีข้อมูลบางอย่างที่มีชื่อผู้ใช้
สิ่งแรกที่เราจะดำเนินการคือค้นหาการแจ้งเตือนที่ค้างอยู่สำหรับผู้ใช้ที่มีชื่อผู้ใช้ชื่อใดชื่อหนึ่ง เราจะรับ registration.getNotifications()
และวนซ้ำและตรวจสอบ notification.data
เพื่อหาชื่อผู้ใช้ที่เฉพาะเจาะจง ดังนี้
const promiseChain = registration.getNotifications().then((notifications) => {
let currentNotification;
for (let i = 0; i < notifications.length; i++) {
if (notifications[i].data && notifications[i].data.userName === userName) {
currentNotification = notifications[i];
}
}
return currentNotification;
});
ขั้นตอนถัดไปคือการแทนที่การแจ้งเตือนนี้ด้วยการแจ้งเตือนใหม่
ในแอปข้อความปลอมนี้ เราจะติดตามจำนวนข้อความใหม่โดยเพิ่มเข้าไปในข้อมูลการแจ้งเตือนใหม่ของเรา และเพิ่มในการแจ้งเตือนใหม่แต่ละรายการ
.then((currentNotification) => {
let notificationTitle;
const options = {
icon: userIcon,
}
if (currentNotification) {
// We have an open notification, let's do something with it.
const messageCount = currentNotification.data.newMessageCount + 1;
options.body = `You have ${messageCount} new messages from ${userName}.`;
options.data = {
userName: userName,
newMessageCount: messageCount
};
notificationTitle = `New Messages from ${userName}`;
// Remember to close the old notification.
currentNotification.close();
} else {
options.body = `"${userMessage}"`;
options.data = {
userName: userName,
newMessageCount: 1
};
notificationTitle = `New Message from ${userName}`;
}
return registration.showNotification(
notificationTitle,
options
);
});
หากมีการแจ้งเตือนแสดงอยู่ในปัจจุบัน เราจะเพิ่มจำนวนข้อความและตั้งชื่อการแจ้งเตือนและเนื้อหาให้สอดคล้องกัน หากไม่มีการแจ้งเตือน เราจะสร้างการแจ้งเตือนใหม่โดยมี newMessageCount
เท่ากับ 1
ผลที่ได้คือข้อความแรกจะมีลักษณะดังนี้
การแจ้งเตือนครั้งที่ 2 จะยุบการแจ้งเตือนเป็นดังนี้
ข้อดีอย่างหนึ่งของวิธีนี้คือหากผู้ใช้เห็นการแจ้งเตือนปรากฏขึ้นมาอย่างใดอย่างหนึ่ง ระบบจะดูสอดคล้องกันมากกว่าการใช้ข้อความล่าสุดแทนการแจ้งเตือน
ข้อยกเว้นสำหรับกฎ
เราได้ระบุว่าคุณต้องแสดงการแจ้งเตือนเมื่อได้รับข้อความ Push ซึ่งเรื่องนี้ เป็นความจริงส่วนใหญ่ สถานการณ์หนึ่งที่คุณไม่จำเป็นต้องแสดงการแจ้งเตือน คือเมื่อผู้ใช้เปิดเว็บไซต์ของคุณและโฟกัส
ภายในกิจกรรมข้อความ Push คุณสามารถตรวจสอบได้ว่าต้องแสดงการแจ้งเตือนหรือไม่ โดยตรวจสอบไคลเอ็นต์ของกรอบเวลาและมองหากรอบเวลาที่โฟกัสไว้
โค้ดสำหรับเรียกดูหน้าต่างทั้งหมดและมองหาหน้าต่างที่โฟกัสมีลักษณะดังนี้
function isClientFocused() {
return clients
.matchAll({
type: 'window',
includeUncontrolled: true,
})
.then((windowClients) => {
let clientIsFocused = false;
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
if (windowClient.focused) {
clientIsFocused = true;
break;
}
}
return clientIsFocused;
});
}
เราใช้ clients.matchAll()
เพื่อรับไคลเอ็นต์หน้าต่างทั้งหมดจากนั้นจึงวนซ้ำเพื่อตรวจสอบพารามิเตอร์ focused
ภายในกิจกรรมข้อความ Push เราจะใช้ฟังก์ชันนี้เพื่อตัดสินใจว่าต้องแสดงการแจ้งเตือนหรือไม่
const promiseChain = isClientFocused().then((clientIsFocused) => {
if (clientIsFocused) {
console.log("Don't need to show a notification.");
return;
}
// Client isn't focused, we need to show a notification.
return self.registration.showNotification('Had to show a notification.');
});
event.waitUntil(promiseChain);
ส่งข้อความถึงหน้าเว็บจากเหตุการณ์พุช
เราเห็นว่าคุณสามารถข้ามการแสดงการแจ้งเตือนได้หากผู้ใช้อยู่ในเว็บไซต์ของคุณ แต่ถ้าคุณยังต้องการแจ้งให้ผู้ใช้ทราบว่าเกิดเหตุการณ์ขึ้น แต่การแจ้งเตือนนั้น ยุ่งยากเกินไป
วิธีหนึ่งคือการส่งข้อความจากโปรแกรมทำงานของบริการ (Service Worker) ไปยังหน้าดังกล่าว วิธีนี้ทำให้หน้าเว็บแสดงการแจ้งเตือนหรือการอัปเดตให้ผู้ใช้ทราบเพื่อแจ้งกิจกรรมดังกล่าว ซึ่งจะเป็นประโยชน์สำหรับสถานการณ์เมื่อมีการแจ้งเตือนเล็กๆ น้อยๆ ในหน้าเว็บที่ดีขึ้นและเป็นมิตรต่อผู้ใช้
สมมติว่าเราได้รับข้อความ Push และตรวจสอบว่าเว็บแอปกำลังมุ่งเน้นอยู่ในปัจจุบัน จากนั้นเราสามารถ "โพสต์ข้อความ" ในหน้าที่เปิดอยู่แต่ละหน้าได้ ดังนี้
const promiseChain = isClientFocused().then((clientIsFocused) => {
if (clientIsFocused) {
windowClients.forEach((windowClient) => {
windowClient.postMessage({
message: 'Received a push message.',
time: new Date().toString(),
});
});
} else {
return self.registration.showNotification('No focused windows', {
body: 'Had to show a notification instead of messaging each page.',
});
}
});
event.waitUntil(promiseChain);
ในแต่ละหน้า เราจะคอยฟังข้อความด้วยการเพิ่มตัวฟังเหตุการณ์
navigator.serviceWorker.addEventListener('message', function (event) {
console.log('Received a message from service worker: ', event.data);
});
ใน Listener ข้อความนี้ คุณจะทำอะไรก็ได้ที่ต้องการ แสดง UI ที่กำหนดเองในหน้าเว็บ หรือจะเพิกเฉยต่อข้อความไปเลยก็ได้
นอกจากนี้ โปรดทราบว่าหากคุณไม่กำหนด Listener ข้อความในหน้าเว็บ ข้อความจากโปรแกรมทำงานของบริการจะไม่ดำเนินการใดๆ
แคชหน้าเว็บและเปิดหน้าต่าง
สถานการณ์หนึ่งที่ไม่อยู่ในขอบเขตของคู่มือนี้แต่ก็คุ้มค่าที่จะพูดคุยกันคือ คุณสามารถปรับปรุง UX โดยรวมของเว็บแอปได้ด้วยการแคชหน้าเว็บที่คุณคาดว่าผู้ใช้จะเข้าชมหลังจากคลิกการแจ้งเตือนของคุณ
ซึ่งต้องตั้งค่า Service Worker ให้รองรับเหตุการณ์ fetch
แต่หากคุณใช้ Listener เหตุการณ์ fetch
อย่าลืมใช้ Listener เหตุการณ์ในเหตุการณ์ push
ด้วยการแคชหน้าเว็บและเนื้อหาที่ต้องใช้ก่อนที่จะแสดงการแจ้งเตือน
ความเข้ากันได้กับเบราว์เซอร์
เหตุการณ์ notificationclose
Clients.openWindow()
ServiceWorkerRegistration.getNotifications()
clients.matchAll()
ดูข้อมูลเพิ่มเติมได้ที่โพสต์ข้อมูลเบื้องต้นเกี่ยวกับ Service Worker
ขั้นตอนถัดไป
- ภาพรวมข้อความ Push ในเว็บ
- วิธีการทำงานของ Push
- การสมัครใช้บริการ
- UX ของสิทธิ์
- การส่งข้อความด้วยไลบรารีพุชจากเว็บ
- โปรโตคอลพุชจากเว็บ
- การจัดการเหตุการณ์พุช
- การแสดงการแจ้งเตือน
- ลักษณะการทำงานของการแจ้งเตือน
- รูปแบบการแจ้งเตือนทั่วไป
- คำถามที่พบบ่อยเกี่ยวกับข้อความ Push
- ปัญหาที่พบได้ทั่วไปและการรายงานข้อบกพร่อง