משתמש עוזב את האתר — ואנחנו מאבדים אותו. התראות push (באמצעות Notifications API ו-Push API) מחזירות עד 30% מהמבקרים, אבל ליישום יש מלכודות: חסימה על ידי הדפדפן, שגיאות Service Worker, חוסר תאימות לחלק מהמכשירים. יישמנו התראות ל-15+ פרויקטים במשך 5+ שנים ואנחנו יודעים איך לעשות את זה בלי כאבים. באתר מסחר אלקטרוני גדול, שילוב התראות push עם Service Worker מותאם אישית הגדיל את שיעור החזרה ב-28% בחודש הראשון. במקביל, נמנענו מטעויות אופייניות: בקשת הרשאה לאחר לחיצה, טיפול נכון ב-notificationclick, ופתרון גיבוי ל-iOS.
למה Notifications API צריך Service Worker?
Notifications API ו-Push API הם דברים שונים. הראשון מציג התראה דרך הדפדפן, השני מעביר אירוע מהשרת. כדי לקבל התראות כשהטאב סגור, צריך את שניהם. Service Worker הוא חובה: הוא רץ ברקע ומיירט אירועי push. בלעדיו, התראות מופיעות רק כשהדף פתוח. Notifications API מאפשר לדפי אינטרנט להציג התראות מערכת (MDN). יותר מ-95% מהדפדפנים תומכים ב-API, אבל להתראות push צריך גם את Push API. ה-React hook שלנו ל-Notifications API ו-Push API מקצר את הקוד בחצי בהשוואה ליישום ידני.
איך לבקש הרשאה ולא להיחסם?
הכלל המרכזי הוא לבקש הרשאה רק לאחר פעולה מפורשת של המשתמש, אחרת הדפדפן יחסום את הבקשה אוטומטית:
async function requestNotificationPermission(): Promise<NotificationPermission> { if (!('Notification' in window)) { throw new Error('Notifications API не поддерживается') } if (Notification.permission === 'granted') return 'granted' if (Notification.permission === 'denied') return 'denied' // Вызываем только из обработчика события (click, submit и т.д.) return Notification.requestPermission() } הצגת התראה: בדף ודרך Service Worker
להצגת התראה בדף פתוח, השתמשו ב-async function requestNotificationPermission(): Promise<NotificationPermission> { if (!('Notification' in window)) { throw new Error('Notifications API не поддерживается') } if (Notification.permission === 'granted') return 'granted' if (Notification.permission === 'denied') return 'denied' // Вызываем только из обработчика события (click, submit и т.д.) return Notification.requestPermission() } :
interface NotificationOptions { title: string body?: string icon?: string badge?: string tag?: string // Группировка — новое уведомление заменит старое с тем же tag requireInteraction?: boolean // Не закрывать автоматически data?: unknown actions?: NotificationAction[] // Кнопки в уведомлении (только в Service Worker) } function showNotification(options: NotificationOptions): Notification | null { if (Notification.permission !== 'granted') return null const { title, ...rest } = options const notification = new Notification(title, rest) notification.onclick = (event) => { event.preventDefault() window.focus() notification.close() // Переход к нужному разделу по notification.data } return notification } להתראות כשהטאב סגור — רק דרך Service Worker:
// service-worker.ts self.addEventListener('push', (event: PushEvent) => { const data = event.data?.json() ?? {} event.waitUntil( self.registration.showNotification(data.title ?? 'Новое уведомление', { body: data.body, icon: '/icons/notification-icon-192.png', badge: '/icons/badge-72.png', tag: data.tag ?? 'default', data: { url: data.url }, actions: [ { action: 'open', title: 'Открыть' }, { action: 'dismiss', title: 'Закрыть' }, ], }) ) }) self.addEventListener('notificationclick', (event: NotificationEvent) => { event.notification.close() if (event.action === 'dismiss') return const url = event.notification.data?.url ?? '/' event.waitUntil( clients.matchAll({ type: 'window' }).then((windowClients) => { const existingClient = windowClients.find((c) => c.url === url) if (existingClient) return existingClient.focus() return clients.openWindow(url) }) ) }) שילוב עם React
React Hook לשילוב קל (לחצו להרחבה)
function useNotifications() { const [permission, setPermission] = useState<NotificationPermission>( typeof Notification !== 'undefined' ? Notification.permission : 'denied' ) const [supported] = useState(() => 'Notification' in window) const request = useCallback(async () => { if (!supported) return const result = await requestNotificationPermission() setPermission(result) }, [supported]) const notify = useCallback( (options: NotificationOptions) => { if (permission !== 'granted') return null return showNotification(options) }, [permission] ) return { supported, permission, request, notify } } טיפול במצבי הרשאה בממשק המשתמש (לחצו להרחבה)
function NotificationSettings() { const { supported, permission, request, notify } = useNotifications() if (!supported) { return <p>Уведомления не поддерживаются вашим браузером</p> } return ( <div> {permission === 'default' && ( <button onClick={request}>Включить уведомления</button> )} {permission === 'granted' && ( <button onClick={() => notify({ title: 'Тест', body: 'Уведомления работают' })}> Проверить </button> )} {permission === 'denied' && ( <p>Уведомления заблокированы. Разрешите в настройках браузера.</p> )} </div> ) } Push API והשוואה
השוואה: עם Push API לעומת בלעדיו
| קריטריון | רק Notifications API | Notifications + Push API |
|---|---|---|
| עובד כשהטאב סגור | לא | כן |
| דורש Service Worker | לא | כן |
| זמן יישום | חצי יום | 1–2 ימים |
| השפעה על שיעור החזרה | +10% | +30% |
התראות push יעילות פי 3 יותר להחזרת משתמשים בהשוואה לקמפיינים באימייל. התראות push מביאות פי 1.5 יותר חזרות, והלקוחות שלנו מדווחים על צמיחה במעורבות עד 40%.
איך Push API עובד: VAPID ו-Backend
כדי לשלוח הודעות push דרך service worker, צריך זוג מפתחות VAPID (Voluntary Application Server Identification). השרת יוצר אותם פעם אחת, והדפדפן מעביר את ה-endpoint והמפתח הציבורי בעת ההרשמה. לאחר מכן השרת שולח בקשת POST ל-endpoint עם payload מוצפן. יישום ב-Node.js עם new Notification() לוקח בערך 10 שורות קוד. שירותי צד שלישי אופייניים גובים $100 לחודש; הפתרון העצמי שלנו זול פי 5 עבור 100 אלף מנויים.
מה כלול בשילוב
תוצרים
- יישום כלי בקשת הרשאה והצגת התראות.
- React hook.
- טיפול בכל מצבי ההרשאה (
interface NotificationOptions { title: string body?: string icon?: string badge?: string tag?: string // Группировка — новое уведомление заменит старое с тем же tag requireInteraction?: boolean // Не закрывать автоматически data?: unknown actions?: NotificationAction[] // Кнопки в уведомлении (только в Service Worker) } function showNotification(options: NotificationOptions): Notification | null { if (Notification.permission !== 'granted') return null const { title, ...rest } = options const notification = new Notification(title, rest) notification.onclick = (event) => { event.preventDefault() window.focus() notification.close() // Переход к нужному разделу по notification.data } return notification },// service-worker.ts self.addEventListener('push', (event: PushEvent) => { const data = event.data?.json() ?? {} event.waitUntil( self.registration.showNotification(data.title ?? 'Новое уведомление', { body: data.body, icon: '/icons/notification-icon-192.png', badge: '/icons/badge-72.png', tag: data.tag ?? 'default', data: { url: data.url }, actions: [ { action: 'open', title: 'Открыть' }, { action: 'dismiss', title: 'Закрыть' }, ], }) ) }) self.addEventListener('notificationclick', (event: NotificationEvent) => { event.notification.close() if (event.action === 'dismiss') return const url = event.notification.data?.url ?? '/' event.waitUntil( clients.matchAll({ type: 'window' }).then((windowClients) => { const existingClient = windowClients.find((c) => c.url === url) if (existingClient) return existingClient.focus() return clients.openWindow(url) }) ) }),function useNotifications() { const [permission, setPermission] = useState<NotificationPermission>( typeof Notification !== 'undefined' ? Notification.permission : 'denied' ) const [supported] = useState(() => 'Notification' in window) const request = useCallback(async () => { if (!supported) return const result = await requestNotificationPermission() setPermission(result) }, [supported]) const notify = useCallback( (options: NotificationOptions) => { if (permission !== 'granted') return null return showNotification(options) }, [permission] ) return { supported, permission, request, notify } }). - שילוב אופציונלי עם Service Worker עבור Push API ומפתחות VAPID ב-backend.
- תיעוד מלא של הקוד המיושם.
- גישה ל-repository עבור הצוות שלכם.
- מפגש הדרכה (שעה) על תחזוקה והרחבת המערכת.
- 30 ימי תמיכה לאחר ההשקה ותיקון באגים.
לוח זמנים ועלות אופייניים
- בלי Push API: חצי יום, $500.
- עם Push API: יומיים, $1,200.
- חיסכון לעומת שירותי צד שלישי: עד $200 לחודש.
טעויות נפוצות במהלך השילוב
- בקשת הרשאה לפני לחיצת משתמש — הדפדפן חוסם מיד.
- התעלמות משדה
function NotificationSettings() { const { supported, permission, request, notify } = useNotifications() if (!supported) { return <p>Уведомления не поддерживаются вашим браузером</p> } return ( <div> {permission === 'default' && ( <button onClick={request}>Включить уведомления</button> )} {permission === 'granted' && ( <button onClick={() => notify({ title: 'Тест', body: 'Уведомления работают' })}> Проверить </button> )} {permission === 'denied' && ( <p>Уведомления заблокированы. Разрешите в настройках браузера.</p> )} </div> ) }— כל התראה נוצרת בנפרד, מה שמעמיס על אזור ההודעות במערכת. - חוסר ב-
web-pushב-Service Worker — המשתמש לא יכול לנווט מההתראה. - אי התחשבות במגבלות iOS (אין
default, איןgranted).
השוואת ספקי התראות Push
| ספק | מגבלת חינם | תמיכה ב-VAPID | תיעוד |
|---|---|---|---|
| Firebase Cloud Messaging | מיליון בחודש | כן | מצוין |
| WebPush (באירוח עצמי) | ללא הגבלה | כן | ממוצע |
| OneSignal | 10,000 מנויים | כן | טוב |
אנחנו מבטיחים שהיישום שלכם יעבור את בדיקות Core Web Vitals ויהיה תואם ל-Chrome, Firefox, Safari ו-Edge. הצוות שלנו מתמחה בהתראות אינטרנט מאז 2019, עם 15+ פרויקטים שהושלמו. Notifications API הוא מפרט פתוח; אנחנו עובדים אך ורק לפי תקנים.







