נבחן כמה דפוסי יישום נפוצים בדחיפת דפי אינטרנט.
צריך להשתמש בכמה ממשקי API שונים שזמינים ב-Service Worker.
אירוע של סגירת התראה
בקטע האחרון ראינו איך אנחנו יכולים להאזין לאירועי notificationclick
.
יש גם אירוע notificationclose
שנקרא אם המשתמש סוגר אחד מהאירועים
התראות (כלומר, במקום ללחוץ על ההתראה, המשתמש לוחץ על ההתראה או מחליק על ההתראה
את ההתראה 'אין מענה כרגע').
האירוע הזה משמש בדרך כלל לניתוח נתונים כדי לעקוב אחרי אינטראקציות של משתמשים עם התראות.
self.addEventListener('notificationclose', function (event) {
const dismissedNotification = event.notification;
const promiseChain = notificationCloseAnalytics();
event.waitUntil(promiseChain);
});
הוספת נתונים להתראה
כאשר מתקבלת הודעת דחיפה, לרוב יש נתונים שימושי אם המשתמש לחץ על ההתראה. לדוגמה, כתובת האתר שאמורות להיפתח כשמשתמש לוחץ על התראה.
הדרך הקלה ביותר לקחת נתונים מאירוע Push ולצרף אותם אל
ההתראה היא להוסיף את הפרמטר 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);
בתוך רכיב handler של קליקים, אפשר לגשת לנתונים באמצעות 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. זה טריק קטן שאספתי מג'ף
Posnick. קריאה אל new URL()
עם האובייקט location
תבצע
מחזירה כתובת אתר מוחלטת אם המחרוזת שמועברת היא יחסית (כלומר, /
תהפוך ל-
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" סוגי לקוחות (כלומר, פשוט מחפשים כרטיסיות וחלונות)
ולהחריג עובדי אינטרנט). includeUncontrolled
מאפשר לנו לחפש
כל הכרטיסיות מהמקור שלך שלא בשליטת השירות הנוכחי
worker, כלומר ה-Service Worker שמריץ את הקוד. באופן כללי,
אני רוצה שהערך של includeUncontrolled
יהיה True תמיד כשקוראים לפונקציה matchAll()
.
אנחנו מתעדים את ההבטחה שהוחזרה בתור 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);
}
});
מיזוג התראות
ראינו שהוספה של תג להתראה מהווה הסכמה להתנהגות שבה כל התראה קיימת עם אותו תג תוחלף.
עם זאת, אפשר להתחכם עוד יותר עם כיווץ ההתראות באמצעות ממשק API של התראות. כדאי לשקול שימוש באפליקציית צ'אט שבה המפתח עשוי לרצות לקבל התראה חדשה הצגת הודעה שדומה ל-"יש לך שתי הודעות מ-י'" במקום להציג רק הודעה.
אפשר לעשות זאת או לשנות את ההתראות הנוכחיות בדרכים אחרות באמצעות 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.
התוצאה היא שההודעה הראשונה תיראה כך:
התראה שנייה תכווץ את ההתראות כך:
היתרון בגישה הזו הוא שאם המשתמש שלכם צופה שמופיעות אחת מעל השנייה, ייראה אחיד יותר מאשר החלפת ההתראה בהודעה האחרונה.
היוצא מן הכלל
אמרתי שחובה להציג התראה כשמקבלים דחיפה, וזאת נכון ברוב הזמן. התרחיש היחיד שבו לא צריך להציג התראה הוא למשתמש יש את האתר פתוח וממוקד.
בתוך אירוע ה-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
.
בתוך אירוע הדחיפה שלנו, נשתמש בפונקציה הזו כדי להחליט אם עלינו להציג התראה:
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);
שליחת הודעה לדף מאירוע Push
ראינו שיש לך אפשרות לדלג על הצגת התראה אם המשתמש נמצא כעת באתר. אבל מה קורה אם עדיין רוצים ליידע את המשתמש שאירוע מסוים התרחש, אבל מוצגת התראה יד כבדה מדי?
אחת הגישות היא לשלוח הודעה מה-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);
בכל אחד מהדפים, אנחנו מקשיבים להודעות על ידי הוספת אירוע של הודעה Listener:
navigator.serviceWorker.addEventListener('message', function (event) {
console.log('Received a message from service worker: ', event.data);
});
בהאזנה להודעה הזו, אתם יכולים לעשות כל מה שתרצו, להציג ממשק משתמש בהתאמה אישית מופעל בדף או להתעלם מההודעה.
כדאי גם לציין שאם לא מגדירים 'מאזינים להודעות' בדף האינטרנט, הודעות מ-Service Worker לא תעשה דבר.
שמירת דף במטמון ופתיחת חלון
תרחיש אחד שלא נכלל במדריך הזה אבל כדאי לדון בו הוא שאפשר לשפר את חוויית המשתמש הכוללת של אפליקציית האינטרנט על ידי שמירה במטמון של דפי אינטרנט שאתם מצפים שהמשתמשים ייכנסו אליהם אחרי לחיצה על ההודעה.
לשם כך צריך להגדיר את Service Worker לטיפול באירועי fetch
,
אבל אם מטמיעים האזנה לאירוע של fetch
, חשוב לוודא
אפשר לנצל אותו באירוע push
על ידי שמירה במטמון של הדף והנכסים
הדרושים להצגת ההתראה.
תאימות דפדפן
האירוע notificationclose
Clients.openWindow()
ServiceWorkerRegistration.getNotifications()
clients.matchAll()
למידע נוסף, אפשר לקרוא את המבוא הזה לעובדי שירות (service worker) פוסט.
מה השלב הבא?
- סקירה כללית של התראות באינטרנט
- כיצד פועלת דחיפת הודעות
- הרשמה של משתמש
- הרשאות UX
- שליחת הודעות באמצעות ספריות דחיפת הודעות מדפי אינטרנט
- פרוטוקול דחיפה באינטרנט
- טיפול באירועי Push
- הצגת התראה
- אופן ההתנהלות של ההתראות
- דפוסים נפוצים של התראות
- שאלות נפוצות בנושא התראות
- בעיות נפוצות ובאגים בדיווח