Реалізація 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 при нестабільному з'єднанні. Зв'яжіться з нами для консультації та замовте реалізацію для вашого проекту.







