مشغّلو الخدمات الجُدد تلقائيًا

tl;dr

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

إذا سبق لك إيقاف التخزين المؤقت لبروتوكول HTTP للنص البرمجي /service-worker.js من خلال عرضه باستخدام Cache-Control: max-age=0، من المفترَض ألا تظهر أي تغييرات بسبب السلوك التلقائي الجديد.

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

الخلفية

في كل مرة تنتقل فيها إلى صفحة جديدة تقع ضمن نطاق مشغّل الخدمات، واستدعِ registration.update() بشكل صريح من JavaScript، أو عندما "يتم تشغيل" عامل الخدمة من خلال حدث push أو sync، سيطلب المتصفّح، في الوقت نفسه، مورد JavaScript الذي تم تمريره في الأصل إلى طلب navigator.serviceWorker.register() للبحث عن التعديلات على النص البرمجي لمشغِّل الخدمات.

لأغراض هذه المقالة، لنفترض أنّ عنوان URL الخاص به هو /service-worker.js وأنّه يتضمّن طلبًا واحدًا للرمز importScripts()، يحمِّل رمزًا إضافيًا يتم تشغيله داخل مشغّل الخدمات:

// Inside our /service-worker.js file:
importScripts('path/to/import.js');

// Other top-level code goes here.

ما الذي سيتغيّر؟

قبل الإصدار 68 من Chrome، كان يتم تقديم طلب تحديث الموقع الإلكتروني /service-worker.js من خلال ذاكرة التخزين المؤقت لبروتوكول HTTP (كما هو الحال في معظم عمليات الجلب). وهذا يعني أنّه إذا تم إرسال النص البرمجي في الأصل باستخدام Cache-Control: max-age=600، لن تصل التحديثات خلال الـ 600 ثانية التالية (10 دقائق) إلى الشبكة، وبالتالي قد لا يتلقّى المستخدم أحدث إصدار من مشغّل الخدمات. أمّا إذا كانت قيمة max-age أكبر من 86400 (24 ساعة)، فسيتم التعامل معها كما لو كانت 86400، لتجنُّب توقّف المستخدمين عن استخدام إصدار معيّن إلى الأبد.

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

updateViaCache

يمكن للمطوّرين الآن ضبط خيار جديد عند طلب navigator.serviceWorker.register(): مَعلمة updateViaCache. ويتم استخدام إحدى القيم الثلاث: 'imports' أو 'all' أو 'none'.

وتحدِّد القيم ما إذا كان سيتم تفعيل ذاكرة التخزين المؤقت لـ HTTP العادية للمتصفّح وكيفية تشغيلها عند إجراء طلب HTTP للتحقق من موارد مشغّل الخدمات المحدَّثة.

  • عند ضبط هذه السياسة على 'imports'، لن يتم مطلقًا الرجوع إلى ذاكرة التخزين المؤقت لبروتوكول HTTP عند البحث عن تحديثات للنص البرمجي /service-worker.js، ولكن ستتم الاستعانة بها عند جلب أي نصوص برمجية تم استيرادها (في المثال path/to/import.js). هذا هو الإعداد التلقائي، ويتطابق مع السلوك الذي يبدأ في Chrome 68.

  • عند ضبط هذه السياسة على 'all'، سيتم الرجوع إلى ذاكرة التخزين المؤقت لبروتوكول HTTP عند إجراء طلبات لكل من نص /service-worker.js ذي المستوى الأعلى وأي نصوص برمجية تم استيرادها داخل مشغّل الخدمة، مثل path/to/import.js. يتوافق هذا الخيار مع السلوك السابق في Chrome، قبل إصدار Chrome 68.

  • عند ضبط هذه السياسة على 'none'، لن يتم الرجوع إلى ذاكرة التخزين المؤقت HTTP عند تقديم طلبات إما إلى /service-worker.js ذات المستوى الأعلى أو لأي نصوص برمجية تم استيرادها، مثل path/to/import.js الافتراضية.

على سبيل المثال، سيسجِّل الرمز التالي أحد مشغّلي الخدمات، وسيتأكّد من عدم الرجوع إلى ذاكرة التخزين المؤقت HTTP عند البحث عن تحديثات للنص البرمجي /service-worker.js أو أي نصوص برمجية تتم الإشارة إليها من خلال importScripts() داخل /service-worker.js:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js', {
    updateViaCache: 'none',
    // Optionally, set 'scope' here, if needed.
  });
}

البحث عن تعديلات على النصوص البرمجية التي تم استيرادها

قبل الإصدار Chrome 78، كان يتم استرداد أي نص برمجي لمشغِّل خدمات يتم تحميله عبر importScripts() مرة واحدة فقط (التحقق أولاً من ذاكرة التخزين المؤقت لـ HTTP أو عبر الشبكة، وفقًا لإعدادات updateViaCache). وبعد هذا الاسترداد الأولي، سيتم تخزينه داخليًا من خلال المتصفح، ولن تتم إعادة جلبه أبدًا.

والطريقة الوحيدة لفرض تطبيق التغييرات على نص برمجي تم استيراده هي تغيير عنوان URL للنص البرمجي، وعادةً ما يكون ذلك من خلال إضافة قيمة نصفية (مثل importScripts('https://example.com/v1.1.0/index.js')) أو من خلال تضمين علامة تجزئة للمحتوى (مثل importScripts('https://example.com/index.abcd1234.js')). ويتمثل التأثير الجانبي لتغيير عنوان URL الذي تم استيراده في تغيير محتوى النص البرمجي لعامل الخدمة ذي المستوى الأعلى، وهو ما يؤدي بدوره إلى تغيير المحتوى في الخدمة.

وبدايةً من الإصدار 78 من Chrome، سيتم إجراء عمليات تحقّق في الوقت نفسه لتحديد ما إذا كان قد تغيّر محتوى أيّ من النصوص البرمجية المستورَدة أم لا في كل مرة يتم فيها البحث عن تحديثات لأحد ملفات عاملي الخدمات ذات المستوى الأعلى. واعتمادًا على عناوين Cache-Control المستخدَمة، قد يتم تنفيذ عمليات فحص النص البرمجي المستورَدة من خلال ذاكرة التخزين المؤقت لـ HTTP في حال ضبط updateViaCache على 'all' أو 'imports' (وهي القيمة التلقائية)، أو قد يتم إجراء عمليات التحقّق مباشرةً ضد الشبكة في حال ضبط updateViaCache على 'none'.

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

يتطابق سلوك Chrome 78 مع ما نفذه Firefox قبل عدة سنوات في Firefox 56. ينفِّذ Safari هذا السلوك أيضًا.

ما هو الإجراء المطلوب من المطوّرين اتّخاذه؟

إذا أوقفت ميزة التخزين المؤقت على HTTP للنص البرمجي /service-worker.js من خلال عرضه باستخدام Cache-Control: max-age=0 (أو قيمة مشابهة)، من المفترَض ألا تظهر لك أي تغييرات بسبب السلوك التلقائي الجديد.

إذا كنت تعرض نص /service-worker.js البرمجي مع تفعيل التخزين المؤقت لـ HTTP، إما عن قصد أو لأنه مجرد الإعداد التلقائي لبيئة الاستضافة، قد تبدأ في رؤية زيادة في طلبات HTTP الإضافية لـ /service-worker.js التي تم إجراؤها على خادمك، وهذه الطلبات هي التي كانت تتم تنفيذها من خلال ذاكرة التخزين المؤقت HTTP. إذا أردت مواصلة السماح لقيمة العنوان Cache-Control بالتأثير في مدى حداثة /service-worker.js، عليك بدء إعداد updateViaCache: 'all' بشكل واضح عند تسجيل مشغّل الخدمات.

بما أنّه قد يكون هناك عدد كبير من المستخدمين في إصدارات المتصفّح القديمة، من الأفضل مواصلة ضبط عنوان HTTP يتضمّن العنصر Cache-Control: max-age=0 في النصوص البرمجية لمشغّل الخدمات، على الرغم من أنّ المتصفّحات الجديدة قد تتجاهلها.

ويمكن لمطوّري البرامج استغلال هذه الفرصة لتحديد ما إذا كانوا يريدون بشكل صريح إيقاف التخزين المؤقت لـ HTTP في نصوصهم البرمجية المستورَدة، وإضافة updateViaCache: 'none' إلى تسجيل مشغِّلي الخدمات إذا كان ذلك مناسبًا.

عرض النصوص البرمجية المستوردة

بدءًا من الإصدار 78 من Chrome، قد يرى المطوّرون المزيد من طلبات HTTP الواردة للموارد التي يتم تحميلها عبر importScripts()، لأنّه سيتم الآن التحقّق منها بحثًا عن التحديثات.

إذا أردت تجنُّب الزيارات الإضافية من HTTP، عليك ضبط عناوين Cache-Control طويلة الأجل عند عرض نصوص برمجية تتضمّن نصوصًا برمجية تتضمن semver أو علامات تجزئة في عناوين URL الخاصة بها، واعتمد على سلوك updateViaCache التلقائي المتّبع في 'imports'.

بدلاً من ذلك، إذا كنت تريد فحص النصوص البرمجية المستورَدة بحثًا عن تحديثات متكررة، تأكّد من عرضها باستخدام Cache-Control: max-age=0، أو استخدام updateViaCache: 'none'.

محتوى إضافي للقراءة

يُنصَح بقراءة "The Service Worker Lifecycle" و"أفضل الممارسات المتعلّقة بالتخزين المؤقت وحلول الحدّ الأقصى للسن المسموح به" من تأليف "جيك أرشيبالد" لجميع المطوّرين الذين ينشرون أي محتوى على الويب.