เราจะมาดูตัวอย่างรูปแบบการใช้งานทั่วไปสำหรับพุชจากเว็บกัน
ซึ่งจะเกี่ยวข้องกับการใช้ API ต่างๆ 2-3 รายการที่มีอยู่ใน Service Worker
เหตุการณ์การปิดการแจ้งเตือน
ในส่วนที่แล้ว เราได้ดูวิธีฟังเหตุการณ์ notificationclick
นอกจากนี้ยังมีเหตุการณ์ notificationclose
ที่เรียกใช้หากผู้ใช้ปิดการแจ้งเตือนของคุณ (นั่นคือ ผู้ใช้คลิกเครื่องหมายกากบาทหรือปัดการแจ้งเตือนออกแทนที่จะคลิกการแจ้งเตือน)
โดยทั่วไปเหตุการณ์นี้ใช้สําหรับข้อมูลวิเคราะห์เพื่อติดตามการมีส่วนร่วมของผู้ใช้กับการแจ้งเตือน
self.addEventListener('notificationclose', function (event) {
const dismissedNotification = event.notification;
const promiseChain = notificationCloseAnalytics();
event.waitUntil(promiseChain);
});
การเพิ่มข้อมูลในการแจ้งเตือน
เมื่อได้รับข้อความ Push เป็นเรื่องปกติที่จะมีข้อมูลที่เป็นประโยชน์เฉพาะเมื่อผู้ใช้คลิกการแจ้งเตือน เช่น URL ที่ควรเปิดขึ้นเมื่อมีการคลิกการแจ้งเตือน
วิธีง่ายที่สุดในการนําข้อมูลจากเหตุการณ์ Push และแนบไปกับการแจ้งเตือนคือการเพิ่มพารามิเตอร์ data
ไปยังออบเจ็กต์ options ที่ส่งไปยัง 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 แบบสัมบูรณ์เพื่อให้จับคู่กับ URL ของกรอบเวลาในภายหลังได้
const urlToOpen = new URL(examplePage, self.location.origin).href;
จากนั้นเราจะได้รายการออบเจ็กต์ WindowClient
ซึ่งเป็นรายการแท็บและหน้าต่างที่เปิดอยู่ในปัจจุบัน (โปรดทราบว่าแท็บเหล่านี้เป็นแท็บสำหรับต้นทางของคุณเท่านั้น)
const promiseChain = clients.matchAll({
type: 'window',
includeUncontrolled: true,
});
ตัวเลือกที่ส่งไปยัง matchAll
จะแจ้งให้เบราว์เซอร์ทราบว่าเราต้องการค้นหาเฉพาะไคลเอ็นต์ประเภท "หน้าต่าง" เท่านั้น (กล่าวคือ ค้นหาเฉพาะแท็บและหน้าต่าง และยกเว้น Web Worker) includeUncontrolled
ช่วยให้เราค้นหาแท็บทั้งหมดจากต้นทางของคุณที่ไม่ได้ควบคุมโดย Service Worker ปัจจุบัน เช่น Service Worker ที่เรียกใช้โค้ดนี้ โดยทั่วไปคุณจะต้องให้ includeUncontrolled
เป็นจริงเสมอเมื่อโทรหา matchAll()
เราบันทึก Promise ที่แสดงผลเป็น promiseChain
เพื่อให้ส่งค่าไปยัง
event.waitUntil()
ในภายหลังได้ ซึ่งจะทำให้ Service Worker ทำงานต่อไปได้
เมื่อการสัญญา 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);
}
});
การรวมการแจ้งเตือน
เราพบว่าการเพิ่มแท็กในการแจ้งเตือนจะเลือกใช้ลักษณะการทำงานที่จะแทนที่การแจ้งเตือนที่มีอยู่ด้วยแท็กเดียวกัน
แต่คุณก็ทำให้การดำเนินการที่ซับซ้อนมากขึ้นได้ด้วยการยุบการแจ้งเตือนโดยใช้ Notification API ลองนึกถึงแอปรับแชทที่นักพัฒนาแอปอาจต้องการให้การแจ้งเตือนใหม่แสดงข้อความที่คล้ายกับ "คุณมีข้อความ 2 ข้อความจาก Matt" แทนที่จะแสดงเฉพาะข้อความล่าสุด
คุณทําเช่นนี้หรือจัดการการแจ้งเตือนปัจจุบันด้วยวิธีอื่นๆ ก็ได้โดยใช้ API ของ registration.getNotifications() ซึ่งจะช่วยให้คุณเข้าถึงการแจ้งเตือนทั้งหมดที่แสดงอยู่ในขณะนี้สําหรับเว็บแอป
มาดูกันว่าเราจะใช้ 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()
เพื่อรับ Window Client ทั้งหมด จากนั้นทำซ้ำเพื่อตรวจสอบพารามิเตอร์ 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 ไปยังหน้าเว็บ วิธีนี้จะช่วยให้หน้าเว็บแสดงการแจ้งเตือนหรือการอัปเดตแก่ผู้ใช้เพื่อแจ้งให้ทราบถึงเหตุการณ์ ซึ่งจะมีประโยชน์ในสถานการณ์ที่การแจ้งเตือนแบบไม่แสดงในหน้าเว็บจะดีกว่าและเหมาะกับผู้ใช้มากกว่า
สมมติว่าเราได้รับการพุช ตรวจสอบว่าตอนนี้เว็บแอปอยู่ในโฟกัส แล้วเราจะ "โพสต์ข้อความ" ไปยังหน้าเว็บที่เปิดอยู่แต่ละหน้าได้ ดังนี้
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);
});
ในโปรแกรมรับฟังข้อความนี้ คุณจะทําสิ่งใดก็ได้ที่ต้องการ เช่น แสดง UI ที่กําหนดเองในหน้าเว็บ หรือละเว้นข้อความไปเลย
นอกจากนี้ โปรดทราบว่าหากคุณไม่ได้กำหนดโปรแกรมรับฟังข้อความในหน้าเว็บ ข้อความจาก Service Worker จะไม่ทำงาน
แคชหน้าเว็บและเปิดหน้าต่าง
สถานการณ์หนึ่งที่อยู่นอกขอบเขตของคู่มือนี้แต่ควรกล่าวถึงคือคุณสามารถปรับปรุง UX โดยรวมของเว็บแอปได้โดยแคชหน้าเว็บที่คาดว่าผู้ใช้จะเข้าชมหลังจากคลิกการแจ้งเตือน
ซึ่งต้องมีการตั้งค่า Service Worker เพื่อจัดการเหตุการณ์ fetch
แต่หากคุณใช้ fetch
event listener โปรดใช้ประโยชน์จากเหตุการณ์ push
โดยการแคชหน้าเว็บและชิ้นงานที่จําเป็นก่อนแสดงการแจ้งเตือน
ความเข้ากันได้กับเบราว์เซอร์
เหตุการณ์ notificationclose
Clients.openWindow()
ServiceWorkerRegistration.getNotifications()
clients.matchAll()
ดูข้อมูลเพิ่มเติมได้ในข้อมูลเบื้องต้นเกี่ยวกับโพสต์ Service Worker
ขั้นตอนถัดไป
- ภาพรวมข้อความ Push บนเว็บ
- วิธีการทำงานของ Push
- การติดตามผู้ใช้
- UX ของสิทธิ์
- การส่งข้อความด้วยไลบรารีพุชบนเว็บ
- Web Push Protocol
- การจัดการเหตุการณ์พุช
- การแสดงการแจ้งเตือน
- ลักษณะการทํางานของการแจ้งเตือน
- รูปแบบการแจ้งเตือนที่พบบ่อย
- คำถามที่พบบ่อยเกี่ยวกับข้อความ Push
- ปัญหาที่พบบ่อยและการรายงานข้อบกพร่อง