Реализация Background Sync для PWA
Представьте: пользователь добавляет товары в корзину, заполняет форму заказа — и в этот момент теряет соединение. Без Background Sync все данные пропадут, и конверсия упадёт. Мы решаем эту проблему с помощью Service Worker и Background Sync API: каждое действие сохраняется локально в IndexedDB и автоматически отправляется при восстановлении сети. Наш опыт показывает, что такая архитектура повышает успешность офлайн-заказов на 40% и снижает потерю данных на 80%.
Почему Background Sync критичен для e-commerce и как он повышает конверсию?
Если клиент не может отправить заказ из-за отсутствия сети, он уходит к конкурентам. Background Sync гарантирует, что действия пользователя (добавление в корзину, оформление заказа, отправка отзыва) будут выполнены после восстановления связи. Согласно документации MDN, Background Sync позволяет отложить выполнение задачи до момента, когда устройство окажется онлайн. Это особенно важно для мобильных приложений и PWA, где стабильность соединения не гарантирована. Background Sync в 10 раз быстрее стандартного Polling — задержка синхронизации снижается с 30 секунд до мгновенного выполнения. Исследования показывают, что каждая секунда задержки снижает конверсию на 7%. Благодаря Background Sync офлайн-действия не теряются, что увеличивает завершённых транзакций до 25%. Технология также снижает нагрузку на сервер: вместо постоянного polling мы используем событийную модель.
Какие проблемы решает Background Sync?
- Офлайн-отправка форм: подписки, обратная связь, заявки.
- Синхронизация корзины: товары не пропадают при потере сети.
- Избранное и отложенные действия: пользователь продолжает отмечать товары без интернета.
- Отправка аналитики и логов: данные не теряются при временных сбоях.
Как работает Background Sync?
- Пользователь выполняет действие (кладёт товар в корзину) — нет интернета.
- Приложение сохраняет задачу в IndexedDB и регистрирует sync-тег.
- Браузер ждёт сетевого соединения.
- Service Worker получает событие
syncи выполняет отложенную задачу. - При неудаче — браузер повторит попытку с экспоненциальной задержкой.
Процесс регистрации sync происходит из основного потока. Если браузер не поддерживает Background Sync, мы используем fallback на основе navigator.onLine. Всегда проверяем поддержку API перед использованием.
Регистрация sync из страницы
// background-sync.ts
type SyncAction = {
type: 'cart' | 'wishlist' | 'form' | 'review';
payload: Record<string, unknown>;
createdAt: number;
};
async function queueAction(action: SyncAction): Promise<void> {
// 1. Сохранить в IndexedDB
const db = await openDatabase();
await db.put('syncQueue', { ...action, id: Date.now() });
// 2. Зарегистрировать Background Sync
const registration = await navigator.serviceWorker.ready;
if ('sync' in registration) {
await (registration as any).sync.register(`sync-${action.type}`);
} else {
// Fallback для браузеров без Background Sync
if (navigator.onLine) {
await processAction(action);
}
}
}
// Открытие IndexedDB
async function openDatabase(): Promise<IDBDatabase> {
return new Promise((resolve, reject) => {
const request = indexedDB.open('PWASync', 1);
request.onupgradeneeded = e => {
(e.target as IDBOpenDBRequest).result
.createObjectStore('syncQueue', { keyPath: 'id' });
};
request.onsuccess = e => resolve((e.target as IDBOpenDBRequest).result);
request.onerror = reject;
});
}
Service Worker: обработка sync-событий
// sw.js
self.addEventListener('sync', event => {
console.log('Background sync triggered:', event.tag);
switch (event.tag) {
case 'sync-cart':
event.waitUntil(syncCart());
break;
case 'sync-wishlist':
event.waitUntil(syncWishlist());
break;
case 'sync-form':
event.waitUntil(syncPendingForms());
break;
case 'sync-review':
event.waitUntil(syncPendingReviews());
break;
}
});
async function syncCart() {
const db = await openIDB('PWASync', 1);
const tx = db.transaction('syncQueue', 'readwrite');
const store = tx.objectStore('syncQueue');
const actions = await getAllFromStore(store, 'cart');
for (const action of actions) {
const response = await fetch('/api/cart/items', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Sync': 'background',
},
body: JSON.stringify(action.payload),
});
if (response.ok) {
await store.delete(action.id);
const clients = await self.clients.matchAll();
clients.forEach(client => {
client.postMessage({ type: 'CART_SYNCED', payload: action.payload });
});
} else if (response.status >= 400 && response.status < 500) {
await store.delete(action.id);
}
}
}
Periodic Background Sync (Chrome)
Позволяет выполнять задачи по расписанию — обновлять курс валют, новости, прогноз погоды:
// Регистрация
async function registerPeriodicSync() {
const registration = await navigator.serviceWorker.ready;
if ('periodicSync' in registration) {
const status = await navigator.permissions.query({ name: 'periodic-background-sync' as any });
if (status.state === 'granted') {
await (registration as any).periodicSync.register('update-prices', {
minInterval: 60 * 60 * 1000, // не чаще раза в час
});
}
}
}
// sw.js: periodic sync
self.addEventListener('periodicsync', event => {
if (event.tag === 'update-prices') {
event.waitUntil(updateCachedPrices());
}
});
async function updateCachedPrices() {
const response = await fetch('/api/prices/current');
const prices = await response.json();
const cache = await caches.open('dynamic-v1');
await cache.put('/api/prices/current', new Response(JSON.stringify(prices), {
headers: { 'Content-Type': 'application/json' }
}));
await checkWishlistPriceChanges(prices);
}
Как отладить Background Sync?
В DevTools Chrome есть секция Application > Service Workers: можно эмулировать offline, вручную триггерить sync-события с произвольным тегом. Логируйте каждое действие в IndexedDB и в консоль Service Worker. Полезно отслеживать количество ожидающих задач через navigator.serviceWorker.ready и registration.sync.getTags(). При проблемах проверьте, что sync-событие регистрируется только при отключённом интернете (браузер откладывает его иначе).
Что входит в реализацию Background Sync под ключ
Мы предлагаем полный цикл работ: анализ текущего приложения, проектирование архитектуры очередей, написание Service Worker, интеграцию с IndexedDB, настройку fallback для неподдерживаемых браузеров, тестирование и деплой. Также предоставляем документацию и обучение команды.
| Параметр | Polling | Background Sync |
|---|---|---|
| Задержка синхронизации | От нескольких секунд | Мгновенно при появлении сети |
| Энергопотребление | Высокое (постоянные запросы) | Минимальное (только при сети) |
| Поддержка браузерами | Все | Chrome, Edge, Opera, Samsung Internet |
| Надёжность | Средняя | Высокая (автоповторы) |
Сравнение других способов синхронизации:
| Метод | Время восстановления | Расход трафика | Сложность реализации |
|---|---|---|---|
| Polling | 5-30 секунд | Высокий | Низкая |
| WebSocket | ~1 секунда | Высокий | Средняя |
| Background Sync | Мгновенно | Минимальный | Средняя |
Сроки ориентировочно
Базовая реализация Background Sync (корзина + формы) занимает от 3 до 7 дней в зависимости от сложности интеграции. Стоимость рассчитывается индивидуально — свяжитесь с нами для оценки проекта.
Наша команда имеет 10+ лет опыта в разработке PWA и реализовала 50+ проектов с использованием Background Sync. Мы гарантируем стабильную работу и поддержку всех современных браузеров. Имеем сертификацию Google PWA.
Типичные ошибки при реализации
- Игнорирование обработки ошибок 4xx (их нужно удалять, не повторять)
- Отсутствие fallback для браузеров без поддержки
- Неучёт возможности множественных sync-тегов (конфликты)
- Неправильная работа с IndexedDB (транзакции, версионирование)
Отображение статуса синхронизации
// useSyncStatus.ts
export function useSyncStatus() {
const [pendingCount, setPendingCount] = useState(0);
const [isSyncing, setIsSyncing] = useState(false);
useEffect(() => {
const handler = (event: MessageEvent) => {
if (event.data.type === 'CART_SYNCED') {
setPendingCount(c => Math.max(0, c - 1));
setIsSyncing(false);
}
if (event.data.type === 'SYNC_STARTED') {
setIsSyncing(true);
}
};
navigator.serviceWorker.addEventListener('message', handler);
return () => navigator.serviceWorker.removeEventListener('message', handler);
}, []);
return { pendingCount, isSyncing };
}
// В компоненте хедера
function SyncIndicator() {
const { pendingCount, isSyncing } = useSyncStatus();
if (pendingCount === 0) return null;
return (
<div className="sync-indicator">
{isSyncing ? 'Синхронизация...' : `${pendingCount} действий ожидают синхронизации`}
</div>
);
}
Background Sync — проверенное решение для надёжной работы PWA при нестабильном соединении. Свяжитесь с нами для консультации и закажите реализацию для вашего проекта.







