PWA离线存IndexedDB后,联网时同步数据至服务端实现方案
Hey there! Let's walk through how to sync your offline-stored IndexedDB data to your backend once the device is back online. This is a core PWA flow, and I'll break it down with actionable steps and code examples to make it straightforward.
First, you need to trigger the sync process when connectivity is restored. You can do this two ways in your Service Worker: listening for the online event, and checking the connection status when the Service Worker activates.
// In your service worker file self.addEventListener('online', async () => { console.log('Back online! Starting data sync...'); await syncPendingData(); }); // Also check on activation in case the SW starts while already online self.addEventListener('activate', async (event) => { if (navigator.onLine) { await syncPendingData(); } });
Next, create a function to pull all the data you stored offline. I'll assume you have an IndexedDB object store named pendingSync that holds items with details like the API endpoint, request method, and payload.
Here's how to read from IndexedDB using promise-based code (no callbacks!):
async function syncPendingData() { // Open your IndexedDB database const db = await openDatabase(); const tx = db.transaction('pendingSync', 'readwrite'); const store = tx.objectStore('pendingSync'); // Get all pending items const pendingItems = await store.getAll(); if (pendingItems.length === 0) { console.log('No pending data to sync.'); db.close(); return; } // Process each item one by one for (const item of pendingItems) { try { // Send to backend await sendToBackend(item); // Delete from IndexedDB if successful await store.delete(item.id); console.log(`Successfully synced item #${item.id}`); } catch (err) { console.error(`Failed to sync item #${item.id}:`, err); // Leave the item in the store for next sync attempt break; // Or continue to the next item—your call based on needs } } await tx.complete; db.close(); } // Helper to open the database (run this once on setup) function openDatabase() { return new Promise((resolve, reject) => { const request = indexedDB.open('MyPWADB', 1); request.onupgradeneeded = (e) => { const db = e.target.result; // Create the pendingSync store if it doesn't exist if (!db.objectStoreNames.contains('pendingSync')) { db.createObjectStore('pendingSync', { keyPath: 'id', autoIncrement: true }); } }; request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); }
Now write a function to actually call your backend endpoints using the stored request details:
async function sendToBackend(item) { const response = await fetch(item.apiUrl, { method: item.method || 'POST', headers: { 'Content-Type': 'application/json', // Add any auth tokens or custom headers your backend requires ...item.headers }, body: JSON.stringify(item.payload) }); // Throw an error if the response isn't successful if (!response.ok) { throw new Error(`Request failed with status: ${response.status}`); } return response.json(); }
For a more robust solution, use the Background Sync API—it's built specifically for this use case. Even if the user closes your app, the browser will wake up your Service Worker when connectivity returns to run the sync.
Step 1: Register a Sync When Saving Offline Data
When you save data to IndexedDB offline, register a background sync event:
async function saveOfflineData(payload, apiUrl, method = 'POST') { const db = await openDatabase(); const tx = db.transaction('pendingSync', 'readwrite'); await tx.objectStore('pendingSync').add({ payload, apiUrl, method }); await tx.complete; db.close(); // Register the sync with the browser try { await self.registration.sync.register('sync-pending-data'); console.log('Background sync registered—we’ll sync when online!'); } catch (err) { console.error('Couldn’t register background sync:', err); } }
Step 2: Listen for the Sync Event in Your Service Worker
self.addEventListener('sync', (event) => { if (event.tag === 'sync-pending-data') { // Tell the browser to wait for the sync to finish event.waitUntil(syncPendingData()); } });
- Conflict Resolution: If your backend might have updated data while the device was offline, add version numbers or timestamps to your data to resolve conflicts (e.g., last-write-wins or user prompts).
- Batch Requests: If you have hundreds of pending items, batch them into smaller chunks to avoid overwhelming your backend.
- Error Logging: Add proper error tracking to monitor sync failures—this helps you debug issues with your API or sync logic.
- Throttle Retries: If sync fails, use exponential backoff (wait longer between retries) to avoid spamming your backend.
内容的提问来源于stack exchange,提问作者prasanna




