تطبيقات الويب التقدمية

المزامنة في الخلفية

17 دقيقة الدرس 9 من 30

المزامنة في الخلفية في تطبيقات الويب التقدمية

تسمح المزامنة في الخلفية لتطبيق الويب التقدمي الخاص بك بتأجيل الإجراءات حتى يحصل المستخدم على اتصال مستقر. يضمن ذلك الحفاظ على إجراءات المستخدم وإكمالها حتى إذا كانت الشبكة غير موثوقة في وقت الإجراء.

ما هي المزامنة في الخلفية؟

المزامنة في الخلفية هي واجهة برمجة تطبيقات ويب تمكن عمال الخدمة من تأجيل المهام حتى يكون لدى المستخدم اتصال مستقر بالإنترنت. إنها مثالية لضمان إكمال إجراءات المستخدم (مثل إرسال الرسائل، أو إرسال النماذج، أو تحميل الملفات) بنجاح.

الفوائد الرئيسية:
  • الموثوقية: تكتمل إجراءات المستخدم حتى بعد فشل الشبكة المؤقت
  • تجربة مستخدم أفضل: لا يحتاج المستخدمون إلى إعادة محاولة الإجراءات الفاشلة يدويًا
  • دعم غير متصل: يتم مزامنة الإجراءات في قائمة الانتظار غير المتصلة عند العودة للاتصال
  • كفاءة البطارية: يدير المتصفح توقيت المزامنة بشكل مثالي

تسجيل حدث المزامنة

اطلب تشغيل حدث مزامنة عندما يكتشف المتصفح اتصالاً مستقرًا:

// في كود التطبيق الرئيسي async function registerSync(tag) { try { const registration = await navigator.serviceWorker.ready; await registration.sync.register(tag); console.log('تم تسجيل المزامنة:', tag); } catch (error) { console.error('فشل تسجيل المزامنة:', error); } } // مثال: وضع رسالة في قائمة الانتظار ليتم إرسالها async function sendMessage(message) { // حفظ الرسالة في IndexedDB await saveToIndexedDB('pending-messages', message); // تسجيل حدث المزامنة if ('sync' in registration) { await registerSync('sync-messages'); console.log('تم وضع الرسالة في قائمة انتظار المزامنة'); } else { // احتياطي: حاول الإرسال على الفور await sendMessageToServer(message); } } // الاستخدام document.getElementById('send-btn').addEventListener('click', async () => { const message = document.getElementById('message-input').value; await sendMessage({ text: message, timestamp: Date.now() }); });

معالجة أحداث المزامنة في عامل الخدمة

استمع لأحداث المزامنة في عامل الخدمة الخاص بك وقم بتنفيذ الإجراءات المؤجلة:

// في sw.js self.addEventListener('sync', (event) => { console.log('تم تشغيل حدث المزامنة:', event.tag); if (event.tag === 'sync-messages') { event.waitUntil(syncMessages()); } else if (event.tag === 'sync-posts') { event.waitUntil(syncPosts()); } }); async function syncMessages() { try { // استرجاع الرسائل المعلقة من IndexedDB const messages = await getFromIndexedDB('pending-messages'); if (messages.length === 0) { console.log('لا توجد رسائل للمزامنة'); return; } // إرسال كل رسالة إلى الخادم for (const message of messages) { try { const response = await fetch('/api/messages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(message) }); if (response.ok) { // إزالة من IndexedDB بعد الإرسال الناجح await removeFromIndexedDB('pending-messages', message.id); console.log('تمت مزامنة الرسالة:', message.id); } else { throw new Error('خطأ في الخادم'); } } catch (error) { console.error('فشل مزامنة الرسالة:', error); // الاحتفاظ في IndexedDB لمحاولة المزامنة التالية } } console.log('اكتملت مزامنة الرسائل'); } catch (error) { console.error('فشلت المزامنة:', error); throw error; // إعادة محاولة المزامنة لاحقًا } }
أفضل ممارسات المزامنة:
  • استخدم أسماء علامات وصفية (مثل 'sync-messages', 'sync-photos')
  • قم بتخزين الإجراءات المعلقة في IndexedDB للاستمرارية
  • قم بإزالة العناصر من قائمة الانتظار فقط بعد المزامنة الناجحة
  • تعامل مع الأخطاء بلطف للسماح بمحاولات إعادة المحاولة
  • نفذ حدود زمنية معقولة

منطق إعادة المحاولة

إذا فشلت محاولة المزامنة، سيعيد المتصفح المحاولة تلقائيًا بناءً على عوامل مختلفة:

self.addEventListener('sync', (event) => { if (event.tag === 'sync-data') { event.waitUntil( syncData() .catch((error) => { console.error('فشلت المزامنة، ستتم إعادة المحاولة:', error); // سيعيد المتصفح المحاولة تلقائيًا // رمي الخطأ للإشارة إلى الفشل throw error; }) ); } }); async function syncData() { const maxRetries = 3; let attempts = 0; while (attempts < maxRetries) { try { const data = await getFromIndexedDB('pending-data'); const response = await fetch('/api/sync', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (response.ok) { await clearIndexedDB('pending-data'); return; // نجاح } attempts++; await new Promise(resolve => setTimeout(resolve, 1000 * attempts)); } catch (error) { attempts++; if (attempts >= maxRetries) { throw error; } } } }
قيود المتصفح: يتحكم المتصفح في وقت إطلاق أحداث المزامنة وقد يؤخرها لتحسين البطارية واستخدام الشبكة. لا تعتمد على إطلاق أحداث المزامنة على الفور أو في أوقات محددة.

مثال كامل: إرسال نموذج دون اتصال

// في التطبيق الرئيسي async function submitForm(formData) { try { // الحفظ في IndexedDB await saveToIndexedDB('pending-forms', { id: Date.now(), data: formData, timestamp: Date.now() }); // تسجيل المزامنة const registration = await navigator.serviceWorker.ready; await registration.sync.register('sync-forms'); // إظهار رسالة النجاح showNotification('تم حفظ النموذج. سيتم الإرسال عند الاتصال.'); } catch (error) { console.error('فشل وضع النموذج في قائمة الانتظار:', error); showNotification('فشل حفظ النموذج. يرجى المحاولة مرة أخرى.'); } } // في sw.js self.addEventListener('sync', (event) => { if (event.tag === 'sync-forms') { event.waitUntil(syncForms()); } }); async function syncForms() { const forms = await getFromIndexedDB('pending-forms'); for (const form of forms) { try { const response = await fetch('/api/submit-form', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(form.data) }); if (response.ok) { await removeFromIndexedDB('pending-forms', form.id); // إظهار الإشعار self.registration.showNotification('تم إرسال النموذج', { body: 'تم إرسال النموذج الخاص بك بنجاح', icon: '/images/success-icon.png' }); } } catch (error) { console.error('فشلت مزامنة النموذج:', error); } } }
تمرين:
  1. أنشئ تطبيق رسائل بسيط يضع الرسائل في قائمة انتظار دون اتصال
  2. نفذ المزامنة في الخلفية لإرسال الرسائل عند العودة للاتصال
  3. قم بتخزين الرسائل المعلقة في IndexedDB
  4. أضف منطق إعادة المحاولة لمحاولات المزامنة الفاشلة
  5. اعرض إشعارات عند مزامنة الرسائل بنجاح
  6. اختبر بالانتقال إلى وضع عدم الاتصال، وإرسال الرسائل، ثم إعادة الاتصال