Push уведомления

Push-уведомления предоставляют возможность отправки сообщений пользователю даже тогда, когда веб-приложение не активно в браузере. В сочетании с современными фреймворками, такими как Lit, это позволяет создавать интерактивные и динамичные интерфейсы с минимальными затратами ресурсов.

Регистрация сервис-воркера

Для реализации Push-уведомлений необходим сервис-воркер, который работает в фоне и обрабатывает входящие уведомления. В Lit-проектах сервис-воркер регистрируется стандартным образом через navigator.serviceWorker:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js')
    .then(registration => {
      console.log('Service Worker зарегистрирован с областью:', registration.scope);
    })
    .catch(error => {
      console.error('Ошибка регистрации Service Worker:', error);
    });
}

Сервис-воркер обеспечивает обработку событий push и notificationclick, что позволяет управлять отображением уведомлений и действиями пользователя.

Подключение Push API

Push API предоставляет методы для подписки на уведомления и взаимодействия с сервером. Подписка создается через объект PushManager, доступный через зарегистрированный сервис-воркер:

async function subscribeUser() {
  const registration = await navigator.serviceWorker.ready;
  try {
    const subscription = await registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY')
    });
    console.log('Подписка на Push уведомления:', subscription);
    // Отправка подписки на сервер
    await fetch('/subscribe', {
      method: 'POST',
      body: JSON.stringify(subscription),
      headers: { 'Content-Type': 'application/json' }
    });
  } catch (error) {
    console.error('Ошибка подписки:', error);
  }
}

Ключевой момент: userVisibleOnly: true обязательный параметр, обеспечивающий отображение уведомлений пользователю.

Интеграция с Lit-компонентами

Lit позволяет интегрировать логику Push-уведомлений непосредственно в компоненты через жизненные циклы и реактивные свойства. Например, можно создать компонент push-button, который инициирует подписку:

import { LitElement, html, css } from 'lit';

class PushButton extends LitElement {
  static styles = css`
    button {
      padding: 0.5rem 1rem;
      font-size: 1rem;
      cursor: pointer;
    }
  `;

  render() {
    return html`
      <button @click=${this.subscribe}>Подписаться на уведомления</button>
    `;
  }

  async subscribe() {
    if ('serviceWorker' in navigator && 'PushManager' in window) {
      const registration = await navigator.serviceWorker.ready;
      const subscription = await registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY')
      });
      await fetch('/subscribe', {
        method: 'POST',
        body: JSON.stringify(subscription),
        headers: { 'Content-Type': 'application/json' }
      });
      console.log('Успешная подписка на уведомления');
    } else {
      console.warn('Push уведомления не поддерживаются этим браузером');
    }
  }
}

customElements.define('push-button', PushButton);

Обработка входящих уведомлений

Сервис-воркер перехватывает события push и отображает уведомления через API showNotification:

self.addEventListener('push', event => {
  const data = event.data.json();
  const options = {
    body: data.body,
    icon: '/icons/notification-icon.png',
    badge: '/icons/badge.png',
    data: { url: data.url }
  };
  event.waitUntil(
    self.registration.showNotification(data.title, options)
  );
});

self.addEventListener('notificationclick', event => {
  event.notification.close();
  event.waitUntil(
    clients.openWindow(event.notification.data.url)
  );
});

Особенности реализации:

  • event.waitUntil гарантирует, что сервис-воркер не завершит выполнение до завершения обработки уведомления.
  • В data можно передавать любые данные для дальнейшей обработки при клике.

Серверная часть и отправка уведомлений

Серверная логика обычно реализуется с использованием VAPID-ключей для аутентификации и Web Push библиотек, таких как web-push на Node.js:

import webPush from 'web-push';

const vapidKeys = {
  publicKey: 'YOUR_PUBLIC_VAPID_KEY',
  privateKey: 'YOUR_PRIVATE_VAPID_KEY'
};

webPush.setVapidDetails(
  'mailto:admin@example.com',
  vapidKeys.publicKey,
  vapidKeys.privateKey
);

const sendNotification = (subscription, payload) => {
  webPush.sendNotification(subscription, JSON.stringify(payload))
    .then(() => console.log('Уведомление отправлено'))
    .catch(err => console.error('Ошибка отправки уведомления', err));
};

Payload содержит данные, которые будут отображены пользователю, включая текст, иконку и ссылки для действий.

Работа с реактивными состояниями и уведомлениями

Lit позволяет связывать состояние компонента с уведомлениями. Например, можно хранить статус подписки в реактивном свойстве и отображать соответствующий интерфейс:

static properties = {
  subscribed: { type: Boolean }
};

async connectedCallback() {
  super.connectedCallback();
  const registration = await navigator.serviceWorker.ready;
  const subscription = await registration.pushManager.getSubscription();
  this.subscribed = !!subscription;
}

Реактивное обновление интерфейса обеспечивает мгновенное отражение изменений, например, смену кнопки с «Подписаться» на «Вы подписаны».

Безопасность и совместимость

  • Обязательное использование HTTPS для Push API.
  • Проверка поддержки браузера для ServiceWorker и PushManager.
  • Обработка ошибок и отказоустойчивость при подписке и отправке уведомлений.
  • Настройка таймаутов и повторных попыток при неудачной отправке уведомлений.

Продвинутые возможности

  • Локальные уведомления через Notification API без участия сервера.
  • Веб-пуш с интерактивными действиями (actions) в уведомлениях.
  • Группировка уведомлений и управление их приоритетом.
  • Аналитика открытия и взаимодействия с уведомлениями для повышения вовлеченности.

Использование Lit в сочетании с Push API позволяет создавать современные, динамичные веб-приложения, где уведомления становятся не просто уведомлением, а частью интерактивного интерфейса с гибкой реактивной логикой.