← Назад к вопросам
Как сделать локальное сообщение через Service Worker?
1.8 Middle🔥 251 комментариев
#JavaScript Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как сделать локальное сообщение через Service Worker
Этот вопрос касается использования Service Workers для отправки локальных (push) уведомлений. Объясню полный процесс от регистрации до отправки сообщения.
1. Что такое Service Worker и зачем он нужен
// Service Worker — это скрипт, который работает в фоне
// независимо от страницы браузера
// Возможности Service Worker:
// 1. Обработка push-уведомлений
// 2. Офлайн-функционал (кэширование)
// 3. Фоновая синхронизация
// 4. Периодические обновления
// Поддержка браузерами:
// Chrome: ДА
// Firefox: ДА
// Safari: ДА (с iOS 16.4+)
// Edge: ДА
2. Шаг 1: Регистрация Service Worker
// main.js или App.jsx
function registerServiceWorker() {
if (!('serviceWorker' in navigator)) {
console.log('Service Workers не поддерживаются браузером');
return;
}
navigator.serviceWorker
.register('/sw.js') // Путь к Service Worker файлу
.then(registration => {
console.log('Service Worker зарегистрирован:', registration);
})
.catch(error => {
console.error('Ошибка регистрации Service Worker:', error);
});
}
// Вызвать при загрузке приложения
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', registerServiceWorker);
} else {
registerServiceWorker();
}
3. Шаг 2: Создать Service Worker файл
// public/sw.js
// Слушать события при установке Service Worker
self.addEventListener('install', (event) => {
console.log('Service Worker установлен');
// Опционально: кэширование файлов
});
// Слушать события при активации
self.addEventListener('activate', (event) => {
console.log('Service Worker активирован');
});
// Слушать push-уведомления
self.addEventListener('push', (event) => {
console.log('Push-уведомление получено:', event);
// Парсить данные из event.data
let notificationData = {
title: 'Стандартное уведомление',
body: 'Нет данных',
icon: '/icon.png',
};
if (event.data) {
try {
notificationData = event.data.json();
} catch (e) {
notificationData.body = event.data.text();
}
}
// Показать уведомление
event.waitUntil(
self.registration.showNotification(notificationData.title, {
body: notificationData.body,
icon: notificationData.icon,
badge: '/badge.png',
tag: 'notification-' + Date.now(),
requireInteraction: false,
})
);
});
// Слушать клики по уведомлению
self.addEventListener('notificationclick', (event) => {
console.log('Клик по уведомлению:', event.notification);
event.notification.close();
// Открыть страницу при клике
event.waitUntil(
clients.matchAll({ type: 'window' }).then(windowClients => {
// Проверить, открыта ли уже такая страница
for (let i = 0; i < windowClients.length; i++) {
if (windowClients[i].url === '/') {
return windowClients[i].focus();
}
}
// Если нет, открыть новое окно
return clients.openWindow('/');
})
);
});
4. Шаг 3: Запросить разрешение и получить subscription
// components/NotificationPermission.jsx
import React, { useState, useEffect } from 'react';
export function NotificationPermission() {
const [subscription, setSubscription] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
checkNotificationPermission();
}, []);
const checkNotificationPermission = async () => {
if (!('serviceWorker' in navigator) || !('Notification' in window)) {
setError('Уведомления не поддерживаются');
return;
}
const registration = await navigator.serviceWorker.ready;
const sub = await registration.pushManager.getSubscription();
if (sub) {
setSubscription(sub);
}
};
const requestNotificationPermission = async () => {
setLoading(true);
setError(null);
try {
// Запросить разрешение
const permission = await Notification.requestPermission();
if (permission !== 'granted') {
setError('Разрешение на уведомления отклонено');
setLoading(false);
return;
}
// Получить регистрацию Service Worker
const registration = await navigator.serviceWorker.ready;
// Получить subscription для push-уведомлений
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: 'YOUR_PUBLIC_VAPID_KEY', // Публичный VAPID ключ
});
setSubscription(subscription);
// Отправить subscription на сервер
await fetch('/api/subscribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(subscription),
});
console.log('Подписка на уведомления успешна');
} catch (err) {
console.error('Ошибка:', err);
setError(err.message);
} finally {
setLoading(false);
}
};
const unsubscribeFromNotifications = async () => {
try {
await subscription.unsubscribe();
setSubscription(null);
console.log('Отписка от уведомлений');
} catch (err) {
console.error('Ошибка отписки:', err);
}
};
return (
<div>
{!subscription ? (
<button
onClick={requestNotificationPermission}
disabled={loading}
>
{loading ? 'Загрузка...' : 'Включить уведомления'}
</button>
) : (
<button onClick={unsubscribeFromNotifications}>
Отключить уведомления
</button>
)}
{error && <p style={{ color: 'red' }}>{error}</p>}
{subscription && <p style={{ color: 'green' }}>Уведомления включены</p>}
</div>
);
}
5. Отправка локального уведомления из основного скрипта
// Прямая отправка уведомления из главного скрипта
export async function showLocalNotification(title, options = {}) {
if (!('serviceWorker' in navigator)) {
console.error('Service Workers не поддерживаются');
return;
}
const registration = await navigator.serviceWorker.ready;
// Показать уведомление через Service Worker
registration.showNotification(title, {
body: options.body || '',
icon: options.icon || '/icon.png',
badge: options.badge || '/badge.png',
tag: options.tag || 'notification',
requireInteraction: options.requireInteraction || false,
actions: options.actions || [],
...options,
});
}
// Использование:
import { showLocalNotification } from '@/lib/notifications';
function MyComponent() {
const handleClick = () => {
showLocalNotification('Привет!', {
body: 'Это локальное уведомление',
icon: '/notification-icon.png',
requireInteraction: true, // Не закрывается автоматически
});
};
return <button onClick={handleClick}>Показать уведомление</button>;
}
6. Отправка уведомления с сервера (Backend)
// Node.js backend пример (web-push библиотека)
const webpush = require('web-push');
// Установить VAPID ключи
const publicKey = process.env.VAPID_PUBLIC_KEY;
const privateKey = process.env.VAPID_PRIVATE_KEY;
const email = process.env.EMAIL;
webpush.setVapidDetails(`mailto:${email}`, publicKey, privateKey);
// Отправить уведомление всем подписчикам
async function sendNotification(subscriptions) {
const payload = JSON.stringify({
title: 'Новое сообщение',
body: 'У вас есть новое сообщение',
icon: '/icon.png',
});
const promises = subscriptions.map(subscription =>
webpush.sendNotification(subscription, payload)
.catch(error => {
if (error.statusCode === 410) {
// Subscription больше не действительна
// Удалить из базы данных
}
})
);
await Promise.all(promises);
}
// API endpoint
app.post('/api/send-notification', async (req, res) => {
const { message } = req.body;
// Получить всех подписчиков из БД
const subscriptions = await getNotificationSubscriptions();
await sendNotification(subscriptions);
res.json({ success: true });
});
7. Генерирование VAPID ключей
# Установить web-push globallу
npm install -g web-push
# Генерировать VAPID ключи
web-push generate-vapid-keys
# Результат:
# Public Key: ...
# Private Key: ...
# Сохранить в .env файл
8. Полный пример компонента
// hooks/useNotifications.js
import { useState, useCallback } from 'react';
export function useNotifications() {
const [supported, setSupported] = useState(
'serviceWorker' in navigator && 'Notification' in window
);
const [permission, setPermission] = useState(Notification.permission);
const requestPermission = useCallback(async () => {
if (!supported) return false;
const perm = await Notification.requestPermission();
setPermission(perm);
return perm === 'granted';
}, [supported]);
const sendNotification = useCallback(
async (title, options = {}) => {
if (!supported || permission !== 'granted') return;
const registration = await navigator.serviceWorker.ready;
registration.showNotification(title, options);
},
[supported, permission]
);
return { supported, permission, requestPermission, sendNotification };
}
// Использование в компоненте
import { useNotifications } from '@/hooks/useNotifications';
export function App() {
const { supported, permission, requestPermission, sendNotification } =
useNotifications();
if (!supported) return <p>Уведомления не поддерживаются</p>;
return (
<div>
{permission !== 'granted' && (
<button onClick={requestPermission}>
Включить уведомления
</button>
)}
{permission === 'granted' && (
<button
onClick={() =>
sendNotification('Привет!', { body: 'Это уведомление' })
}
>
Показать уведомление
</button>
)}
</div>
);
}
Важные моменты
- HTTPS обязателен - Service Worker работает только на HTTPS (или localhost)
- VAPID ключи - нужны для идентификации приложения при отправке push
- Разрешение пользователя - нужно явное разрешение на уведомления
- Обработка ошибок - subscription может истечь и нужно удалить из БД
- Тестирование - используй Chrome DevTools -> Application -> Service Workers
Service Workers с push-уведомлениями — мощный инструмент для engagement, но требует внимательной реализации и обработки ошибок.