إنشاء تجارب بحث مرنة باستخدام Workbox

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

قياس

قبل إضافة تحسينات، من الأفضل دائمًا تحليل الحالة الحالية للتطبيق أولاً.

  • انقر على إنشاء ريمكس لتعديله ليصبح المشروع قابلاً للتعديل.
  • لمعاينة الموقع الإلكتروني، اضغط على عرض التطبيق، ثم اضغط على ملء الشاشة ملء الشاشة.

في علامة التبويب الجديدة التي فتحتها للتو، تحقق من سلوك موقع الويب عند عدم الاتصال بالإنترنت:

  1. اضغط على "Control+Shift+J" (أو "Command+Option+J" على نظام التشغيل Mac) لفتح "أدوات مطوّري البرامج".
  2. انقر على علامة التبويب الشبكة.
  3. افتح "أدوات مطوري البرامج في Chrome" واختَر لوحة "الشبكة".
  4. في القائمة المنسدلة التقييد، اختر بلا اتصال.
  5. في التطبيق التجريبي، أدخِل طلب بحث، ثم انقر على الزر بحث.

يتم عرض صفحة الخطأ العادية في المتصفّح:

لقطة شاشة لتجربة المستخدم التلقائية بلا إنترنت في المتصفّح.

تقديم رد احتياطي

يحتوي مشغّل الخدمات على رمز برمجي لإضافة الصفحة بلا اتصال بالإنترنت إلى قائمة التخزين المؤقت، بحيث يمكن تخزينها مؤقتًا دائمًا في حدث install لمشغِّل الخدمات.

تحتاج عادةً إلى توجيه Workbox لإضافة هذا الملف إلى قائمة التخزين المؤقت المُسبَق في وقت الإصدار، من خلال دمج المكتبة مع أداة التصميم التي تختارها (مثل webpack أو gulp).

ولتبسيط الأمر، قمنا بذلك بالفعل. ويؤدي الرمز التالي في public/sw.js إلى تنفيذ ذلك:

const FALLBACK_HTML_URL = '/index_offline.html';
…
workbox.precaching.precacheAndRoute([FALLBACK_HTML_URL]);

بعد ذلك، أضِف رمزًا لاستخدام الصفحة المتوفّرة بلا إنترنت كاستجابة احتياطية:

  1. للاطّلاع على المصدر، اضغط على عرض المصدر.
  2. أضِف الرمز التالي إلى أسفل public/sw.js:
workbox.routing.setDefaultHandler(new workbox.strategies.NetworkOnly());

workbox.routing.setCatchHandler(({event}) => {
  switch (event.request.destination) {
    case 'document':
      return caches.match(FALLBACK_HTML_URL);
      break;
    default:
      return Response.error();
  }
});

وتؤدي التعليمة البرمجية إلى ما يلي:

  • يتم تحديد استراتيجية الشبكة فقط التلقائية التي سيتم تطبيقها على جميع الطلبات.
  • تشير هذه السمة إلى معالِج عام للأخطاء من خلال طلب workbox.routing.setCatchHandler() لإدارة الطلبات التي تعذّر تنفيذها. عندما تكون الطلبات خاصة بالمستندات، سيتم عرض صفحة HTML احتياطية بلا اتصال بالإنترنت.

لاختبار هذه الوظيفة:

  1. ارجع إلى علامة التبويب الأخرى التي تشغّل تطبيقك.
  2. أعِد ضبط القائمة المنسدلة التقييد على متصل.
  3. اضغط على الزر رجوع في Chrome للعودة إلى صفحة البحث.
  4. احرص على أن يكون مربّع الاختيار إيقاف ذاكرة التخزين المؤقت في "أدوات مطوّري البرامج" غير مفعّل.
  5. اضغط مع الاستمرار على زر إعادة التحميل في Chrome واختَر إفراغ ذاكرة التخزين المؤقت وإعادة التحميل الكامل للتأكّد من أنّه تم تحديث مشغّل الخدمات.
  6. أعِد ضبط القائمة المنسدلة تقييد على بلا إنترنت مرة أخرى.
  7. أدخِل طلب بحث، ثم انقر على زر البحث مرة أخرى.

يتم عرض صفحة HTML الاحتياطية:

لقطة شاشة لتجربة المستخدم المخصّصة بلا إنترنت في المتصفّح.

طلب إذن إرسال الإشعارات

ولتبسيط الأمر، تحتوي الصفحة المتوفّرة بلا إنترنت على views/index_offline.html على رمز لطلب أذونات إرسال الإشعارات في كتلة نص برمجي في أسفل الصفحة:

function requestNotificationPermission(event) {
  event.preventDefault();

  Notification.requestPermission().then(function (result) {
    showOfflineText(result);
  });
}

وتؤدي التعليمة البرمجية إلى ما يلي:

  • عندما ينقر المستخدم على الاشتراك في الإشعارات، يتم استدعاء الوظيفة requestNotificationPermission()، التي تستدعي Notification.requestPermission()، لعرض الطلب التلقائي لإذن المتصفّح. يتم حلّ الوعد باستخدام الإذن الذي يختاره المستخدم، والذي يمكن أن يكون granted أو denied أو default.
  • يؤدي هذا الخيار إلى تمرير الإذن الذي تم حلّه إلى showOfflineText() لعرض النص المناسب للمستخدم.

إنشاء طلبات بحث بلا اتصال بالإنترنت وإعادة المحاولة عند استعادة الاتصال بالإنترنت

بعد ذلك، يمكنك تنفيذ مزامنة الخلفية في Workspace لمواصلة طلبات البحث بلا اتصال بالإنترنت، حتى يمكن إعادة محاولة الاتصال عندما يكتشف المتصفِّح استعادة إمكانية الاتصال.

  1. فتح public/sw.js للتعديل
  2. أضف التعليمة البرمجية التالية في نهاية الملف:
const bgSyncPlugin = new workbox.backgroundSync.Plugin('offlineQueryQueue', {
  maxRetentionTime: 60,
  onSync: async ({queue}) => {
    let entry;
    while ((entry = await queue.shiftRequest())) {
      try {
        const response = await fetch(entry.request);
        const cache = await caches.open('offline-search-responses');
        const offlineUrl = `${entry.request.url}&notification=true`;
        cache.put(offlineUrl, response);
        showNotification(offlineUrl);
      } catch (error) {
        await this.unshiftRequest(entry);
        throw error;
      }
    }
  },
});

وتؤدي التعليمة البرمجية إلى ما يلي:

  • يتضمّن workbox.backgroundSync.Plugin منطق إضافة الطلبات التي تعذّر تنفيذها إلى قائمة الانتظار لإعادة المحاولة لاحقًا. ستظل هذه الطلبات محفوظة في IndexedDB.
  • تشير السمة maxRetentionTime إلى المدة الزمنية التي يمكن أن تتم خلالها إعادة محاولة إرسال الطلب. في هذه الحالة، اخترنا 60 دقيقة (وبعدها سيتم تجاهلها).
  • onSync هو الجزء الأكثر أهمية في هذا الرمز. سيتم طلب معاودة الاتصال هذه عند عودة الاتصال ليتم استرداد الطلبات في قائمة الانتظار ثم جلبها من الشبكة.
  • تتمّ إضافة استجابة الشبكة إلى ذاكرة التخزين المؤقت offline-search-responses، مع إلحاق مَعلمة طلب البحث &notification=true لكي يمكن اختيار إدخال ذاكرة التخزين المؤقت هذا عندما ينقر المستخدِم على الإشعار.

لدمج المزامنة في الخلفية مع خدمتك، حدِّد استراتيجية NetworkOnly للطلبات الواردة إلى عنوان URL للبحث (/search_action) واضبط السمة bgSyncPlugin المحدّدة مسبقًا. أضِف الرمز التالي إلى أسفل public/sw.js:

const matchSearchUrl = ({url}) => {
  const notificationParam = url.searchParams.get('notification');
  return url.pathname === '/search_action' && !(notificationParam === 'true');
};

workbox.routing.registerRoute(
  matchSearchUrl,
  new workbox.strategies.NetworkOnly({
    plugins: [bgSyncPlugin],
  }),
);

يؤدي هذا إلى إعلام Workbox بالانتقال دائمًا إلى الشبكة، وعندما تخفق الطلبات، استخدم منطق مزامنة الخلفية.

بعد ذلك، أضِف الرمز التالي إلى أسفل public/sw.js لتحديد استراتيجية تخزين مؤقت للطلبات الواردة من الإشعارات. استخدِم استراتيجية CacheFirst، حتى يمكن عرضها من ذاكرة التخزين المؤقت.

const matchNotificationUrl = ({url}) => {
  const notificationParam = url.searchParams.get('notification');
  return (url.pathname === '/search_action' && (notificationParam === 'true'));
};

workbox.routing.registerRoute(matchNotificationUrl,
  new workbox.strategies.CacheFirst({
     cacheName: 'offline-search-responses',
  })
);

أخيرًا، أضف الرمز لإظهار الإشعارات:

function showNotification(notificationUrl) {
  if (Notification.permission) {
     self.registration.showNotification('Your search is ready!', {
        body: 'Click to see you search result',
        icon: '/img/workbox.jpg',
        data: {
           url: notificationUrl
        }
     });
  }
}

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

اختبار الميزة

  1. ارجع إلى علامة التبويب الأخرى التي تشغّل تطبيقك.
  2. أعِد ضبط القائمة المنسدلة التقييد على متصل.
  3. اضغط على الزر رجوع في Chrome للعودة إلى صفحة البحث.
  4. اضغط مع الاستمرار على زر إعادة التحميل في Chrome واختَر إفراغ ذاكرة التخزين المؤقت وإعادة التحميل الكامل للتأكّد من أنّه تم تحديث مشغّل الخدمات.
  5. أعِد ضبط القائمة المنسدلة تقييد على بلا إنترنت مرة أخرى.
  6. أدخِل طلب بحث، ثم انقر على زر البحث مرة أخرى.
  7. انقر على الاشتراك في الإشعارات.
  8. عندما يسألك Chrome عما إذا كنت تريد منح التطبيق إذنًا بإرسال الإشعارات، انقر على السماح.
  9. أدخِل طلب بحث آخر وانقر على الزر بحث مرة أخرى.
  10. أعِد ضبط القائمة المنسدلة التقييد على متصل مرة أخرى.

بعد عودة الاتصال، سيظهر إشعار:

لقطة شاشة للتدفق الكامل بلا اتصال بالإنترنت.

الخلاصة

يوفّر Workbox العديد من الميزات المدمجة لجعل تطبيقات PWA أكثر مرونة وتفاعلاً. في هذا الدرس التطبيقي حول الترميز، استكشفت كيفية تنفيذ واجهة برمجة التطبيقات Background Sync API عن طريق تجريد Workbox، لضمان عدم فقدان طلبات بحث المستخدمين بلا اتصال بالإنترنت، وإمكانية إعادة المحاولة عند عودة الاتصال بالإنترنت. الإصدار التجريبي هو تطبيق بحث بسيط، ولكن يمكنك استخدام طريقة تنفيذ مشابهة لسيناريوهات وحالات استخدام أكثر تعقيدًا، بما في ذلك تطبيقات المحادثة ونشر الرسائل على الشبكات الاجتماعية وما إلى ذلك.