جارٍ العرض

من الجوانب الرئيسية لتطبيقات الويب التقدّمية أنّها موثوقة، ويمكنها تحميل مواد العرض بسرعة، ما يحافظ على تفاعل المستخدمين ويقدّم لهم الملاحظات على الفور، حتى في ظروف الشبكة السيئة. كيف يمكن حدوث ذلك؟ بفضل حدث الخدمة fetch.

Browser Support

  • Chrome: 40.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 11.1.

Source

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

يتلقّى معالِج fetch جميع الطلبات الواردة من التطبيق، بما في ذلك عناوين URL وعناوين HTTP، ويسمح لمطوّر التطبيق بتحديد كيفية معالجتها.

يُعدّ "عامل الخدمة" وسيطًا بين العميل والشبكة.

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

self.addEventListener("fetch", event => {
    console.log(`URL requested: ${event.request.url}`);
});

الردّ على طلب

عندما يصل طلب إلى مشغّل الخدمة، يمكنك إجراء أمرَين: تجاهله، ما يؤدي إلى إرساله إلى الشبكة، أو الردّ عليه. من خلال الردّ على الطلبات من داخل الخدمة العاملة، يمكنك اختيار المحتوى الذي سيتم إرجاعه إلى تطبيقك المتوافق مع الأجهزة الجوّالة (PWA) وطريقة إرجاعه، حتى عندما يكون المستخدم غير متصل بالإنترنت.

للردّ على طلب وارد، يمكنك استدعاء event.respondWith() من داخل معالِج حدث fetch، على النحو التالي:

// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
    const response = .... // a response or a Promise of response
    event.respondWith(response);
});

يجب استدعاء respondWith() بشكل متزامن ويجب عرض عنصر Response. ولكن لا يمكنك استدعاء respondWith() بعد انتهاء معالِج حدث الجلب، مثل ضمن مكالمة غير متزامنة. إذا كنت بحاجة إلى الانتظار للحصول على الردّ الكامل، يمكنك تمرير وعد إلى respondWith() يتم حلّه باستخدام Response.

إنشاء الردود

بفضل Fetch API، يمكنك إنشاء ردود HTTP في رمز JavaScript، ويمكن تخزين هذه الردود مؤقتًا باستخدام واجهة برمجة التطبيقات Cache Storage API وإعادتها كما لو كانت واردة من خادم ويب.

لإنشاء ردّ، أنشئ عنصرًا جديدًا من النوع Response، واضبط نصه وخياراته، مثل الحالة والعناوين:

const simpleResponse = new Response("Body of the HTTP response");

const options = {
   status: 200,
   headers: {
    'Content-type': 'text/html'
   }
};
const htmlResponse = new Response("<b>HTML</b> content", options)

الاستجابة من ذاكرة التخزين المؤقت

الآن بعد أن عرفت كيفية عرض استجابات HTTP من أحد موظّفي الخدمة، حان وقت استخدام واجهة التخزين المؤقت لتخزين مواد العرض على الجهاز.

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

تتلقّى الدالة match() طلب HTTP أو عنوان URL كوسيطة، وتُعرِض عملية غير مكتملة يتم حلّها باستخدام الردّ المرتبط بالمفتاح المقابل.

// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
   console.log(response ? response : "It's not in the cache");
});

// Cache-specific search
caches.open("pwa-assets").then(cache => {
  cache.match(urlOrRequest).then(response => {
    console.log(response ? response : "It's not in the cache");
  });
});

استراتيجيات التخزين المؤقت

لا يناسب عرض الملفات من ذاكرة التخزين المؤقت للمتصفّح كل حالات الاستخدام. على سبيل المثال، يمكن للمستخدم أو المتصفّح إزالة ذاكرة التخزين المؤقت. لهذا السبب، عليك تحديد استراتيجياتك الخاصة لإرسال مواد العرض إلى تطبيقك المتوافق مع الأجهزة الجوّالة. يمكنك استخدام أكثر من استراتيجية واحدة لتخزين المحتوى المؤقت. ويمكنك تحديد قواعد مختلفة لأنماط عناوين URL المختلفة. على سبيل المثال، يمكنك استخدام استراتيجية واحدة لأقل عدد من مواد عرض واجهة المستخدم، واستراتيجية أخرى لطلبات بيانات واجهة برمجة التطبيقات، واستراتيجية ثالثة لعناوين URL للصور والبيانات. لإجراء ذلك، اقرأ event.request.url في ServiceWorkerGlobalScope.onfetch وحلِّله من خلال التعبيرات العادية أو نمط عنوان URL. (في وقت كتابة هذه المقالة، لم يكن نمط عنوان URL متوافقًا مع جميع المنصات).

في ما يلي الاستراتيجيات الأكثر شيوعًا:

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

ذاكرة التخزين المؤقت أولاً

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

استراتيجية &quot;الاستناد إلى ذاكرة التخزين المؤقت أولاً&quot;

self.addEventListener("fetch", event => {
   event.respondWith(
     caches.match(event.request)
     .then(cachedResponse => {
       // It can update the cache to serve updated content on the next request
         return cachedResponse || fetch(event.request);
     }
   )
  )
});

الشبكة أولاً

هذه الاستراتيجية هي نسخة طبق الأصل من استراتيجية "الخزينة أولاً"، فهي تتحقّق مما إذا كان يمكن تنفيذ الطلب من الشبكة، وإذا تعذّر ذلك، تحاول استرجاعه من ذاكرة التخزين المؤقت. مثل ذاكرة التخزين المؤقت أولاً. في حال عدم توفّر استجابة من الشبكة أو من ذاكرة التخزين المؤقت، سيحدث خطأ في الطلب. عادةً ما يكون الحصول على الاستجابة من الشبكة أبطأ من الحصول عليها من ذاكرة التخزين المؤقت، وتمنح هذه الاستراتيجية الأولوية للمحتوى المعدَّل بدلاً من الأداء.

استراتيجية &quot;الشبكة أولاً&quot;

self.addEventListener("fetch", event => {
   event.respondWith(
     fetch(event.request)
     .catch(error => {
       return caches.match(event.request) ;
     })
   );
});

غير صالحة أثناء إعادة التحقّق

تُرجِع استراتيجية "البيانات القديمة أثناء إعادة التحقّق من الصحة" استجابة محفوظة في ذاكرة التخزين المؤقت على الفور، ثم تتحقّق من الشبكة بحثًا عن تحديث، وتستبدل الاستجابة المحفوظة في ذاكرة التخزين المؤقت إذا تم العثور على تحديث. تُجري هذه الاستراتيجية طلبًا على الشبكة دائمًا، لأنّه حتى إذا تم العثور على مورد مخزّن مؤقتًا، ستحاول تعديل ما كان في ذاكرة التخزين المؤقت بما تم استلامه من الشبكة، لاستخدام النسخة المعدّلة في الطلب التالي. وبالتالي، توفّر لك هذه الاستراتيجية طريقة للاستفادة من العرض السريع لاستراتيجية "الاستناد إلى ذاكرة التخزين المؤقت أولاً" وتعديل ذاكرة التخزين المؤقت في الخلفية.

استراتيجية &quot;البيانات القديمة أثناء إعادة التحقّق من الصحة&quot;

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cachedResponse => {
        const networkFetch = fetch(event.request).then(response => {
          // update the cache with a clone of the network response
          const responseClone = response.clone()
          caches.open(url.searchParams.get('name')).then(cache => {
            cache.put(event.request, responseClone)
          })
          return response
        }).catch(function (reason) {
          console.error('ServiceWorker fetch failed: ', reason)
        })
        // prioritize cached response over network
        return cachedResponse || networkFetch
      }
    )
  )
})

الشبكة فقط

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

استراتيجية &quot;الشبكة فقط&quot;

ذاكرة التخزين المؤقت فقط

تضمن استراتيجية "التخزين المؤقت فقط" عدم إرسال الطلبات إلى الشبكة مطلقًا، ويتم الردّ على جميع الطلبات الواردة باستخدام عنصر تخزين مؤقت مملوء مسبقًا. يستخدم الرمز البرمجي التالي معالِج الأحداث fetch مع الطريقة match لتخزين ذاكرة التخزين المؤقت للردّ على ذاكرة التخزين المؤقت فقط:

self.addEventListener("fetch", event => {
   event.respondWith(caches.match(event.request));
});

استراتيجية التخزين المؤقت فقط

الاستراتيجيات المخصّصة

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

على سبيل المثال، يمكنك استخدام استراتيجية "الشبكة أولاً" مع مهلة لمنح الأولوية للمحتوى المعدَّل، ولكن فقط إذا ظهر الردّ خلال حدّ معيّن تحدّده. يمكنك أيضًا دمج استجابة محفوظة مؤقتًا مع استجابة من الشبكة وإنشاء استجابة معقّدة من الخدمة العاملة.

تعديل مواد العرض

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

الموارد