طريقة التفكير في عمال الخدمة.
فعاملو الخدمة أقوياء ويستحقون التعلّم تمامًا. تتيح لك تقديم مستوى جديد تمامًا من التجربة للمستخدمين. يمكن تحميل موقعك الإلكتروني بشكل فوري. يمكن أن تعمل بلا اتصال بالإنترنت. يمكن تثبيته كتطبيق خاص بنظام التشغيل ويتمتع بمظهر مصقول، ولكن مع إمكانية الوصول وحرية الويب.
غير أنّ عاملي الخدمة يختلفون عن أي شيء اعتاد عليه معظم مطوّري برامج الويب. تتميّز هذه الألعاب بفرص تعلُّمها وحدوث عقبات يجب الانتباه إليها.
لقد تعاونت أنا وGoogle Developers مؤخرًا على مشروع—Service Workies، وهو لعبة مجانية لفهم العاملين في الخدمات. أثناء إنشائه والعمل مع التفاصيل المعقدة للعاملين في الخدمات، واجهت بعض العقبات. أكثر ما ساعدني هو ابتكار عدد قليل من الاستعارات التصويرية. سوف نستكشف في هذه المشاركة هذه النماذج الذهنية ونحضّر أدمغتنا لسمات المتناقضة التي تجعل موظفي الخدمة معقدين ورائعين على حد سواء.
نفس الشيء، لكن مختلفًا
أثناء ترميز مشغّل الخدمات، ستشعر بأنّ العديد من الأمور مألوفة لك. يمكنك استخدام ميزات لغة JavaScript الجديدة المفضّلة لديك. ويمكنك الاستماع إلى الأحداث في مراحل النشاط، كما هو الحال مع الأحداث في واجهة المستخدم. ويمكنك التحكّم في مسار التحكّم من خلال تنفيذ وعود مثل ما اعتدت عليه.
غير أن سلوك مشغّل الخدمات الآخر يتسبب في تشتيت انتباهك. وخاصةً عند إعادة تحميل الصفحة مع عدم تطبيق التغييرات على الرمز.
طبقة جديدة
عادةً ما يكون هناك طبقتان يجب التفكير فيهما عند إنشاء موقع إلكتروني، وهما: العميل والخادم. عامل الخدمة هو طبقة جديدة تمامًا تقع في المنتصف.
يمكنك اعتبار مشغّل الخدمات نوعًا من إضافات المتصفّحات، أي إضافة يمكن لموقعك الإلكتروني تثبيتها في متصفّح المستخدم. وبعد تثبيت مشغّل الخدمات، يعمل عامل الخدمة على توسيع المتصفّح لموقعك الإلكتروني من خلال طبقة وسط قوية. ويمكن لطبقة عامل الخدمة هذه اعتراض ومعالجة جميع الطلبات التي يرسلها موقعك الإلكتروني.
يكون لطبقة عامل الخدمة دورة حياة خاصة بها بشكل مستقل عن علامة تبويب المتصفّح. لا يكفي إجراء تحديث بسيط للصفحة لتحديث مشغّل الخدمات، تمامًا كما لو كنت لا تتوقع أن تؤدي عملية إعادة تحميل الصفحة إلى تحديث الرمز البرمجي المنشور على الخادم. ولكل طبقة قواعدها الفريدة للتحديث.
في لعبة Service Workies، نتناول الكثير من التفاصيل حول مراحل نشاط مشغّل الخدمات ونمنحك الكثير من التدريب في العمل عليها.
فعّالة، ولكنها محدودة
يمنحك وجود عامل خدمة على موقعك فوائد مذهلة. يمكن لموقعك الإلكتروني تنفيذ ما يلي:
- تعمل بشكل لا تشوبه شائبة حتى عندما يكون المستخدم غير متصل
- تحقيق تحسينات كبيرة في الأداء من خلال التخزين المؤقت
- استخدام الإشعارات الفورية
- تثبيت كتطبيق PWA
بقدر ما يستطيع عاملو الخدمة، فإنهم مقيدون بالتصميم. ولا يمكنها تنفيذ أي إجراء بشكل متزامن أو في سلسلة المحادثات نفسها التي يعمل بها موقعك الإلكتروني. وهذا يعني عدم إمكانية الوصول إلى:
- localStorage
- نموذج DOM
- النافذة
الخبر السار هو أنّ هناك بعض الطرق التي يمكن لصفحتك من خلالها التواصل مع مشغّل الخدمات، بما في ذلك postMessage
المباشر، وقنوات الرسائل الفردية، وقنوات البث الفردية.
طويلة الأجل، ولكنها قصيرة الأجل
ويبقى عامل الخدمة النشط نشطًا حتى بعد أن يغادر المستخدم موقعك الإلكتروني أو يغلق علامة التبويب. ويحتفظ المتصفّح بعامل تشغيل هذه الخدمة حتى يكون جاهزًا في المرة التالية التي يعود فيها المستخدم إلى موقعك. قبل إجراء الطلب الأول، يحصل عامل الخدمة على فرصة لاعتراضه والتحكم في الصفحة. وهذا ما يسمح للموقع الإلكتروني بالعمل بلا اتصال بالإنترنت، حيث يمكن لعامل الخدمة عرض نسخة مخزَّنة مؤقتًا من الصفحة نفسها، حتى إذا لم يكن لدى المستخدم اتصال بالإنترنت.
في Service Workies، ننشئ هذا المفهوم من خلال Kolohe (عامل خدمة ودود) يعترض الطلبات ويعالجها.
متوقفة
على الرغم من أنّ عاملي الخدمة يبدون غير منقطعين بعد، يمكن إيقافهم في أي وقت تقريبًا. لا يريد المتصفح إهدار الموارد على عامل خدمة لا يفعل أي شيء في الوقت الحالي. إنّ إيقاف الخدمة ليس مثل إنهاء الخدمة، لأنّ عامل الخدمة يظل مثبّتًا ومفعَّلاً. خَلَّصْ مَوْعِدُهْ لِلنَّوْمْ. وعند الحاجة في المرة التالية (على سبيل المثال، لمعالجة طلب)، ينشطه المتصفّح من جديد.
waitUntil
ونظرًا لاحتمالية الخلود إلى النوم باستمرار، يحتاج عامل الخدمة إلى وسيلة يستطيع من خلالها إعلام المتصفّح بأنّه ينفّذ نشاطًا مهمًا ولا يشعر بأخذ قيلولة. وهنا يأتي دور event.waitUntil()
. وتعمل هذه الطريقة على إطالة دورة حياتها، مما يبقيها متوقفة ومن الانتقال إلى المرحلة التالية من دورة حياتها إلى أن نكون مستعدين. وهذا يمنحنا الوقت الكافي لإعداد ذاكرات التخزين المؤقت وجلب الموارد من الشبكة وما إلى ذلك.
يوضّح هذا المثال المتصفّح أنّ مشغّل الخدمات لم ينتهِ من التثبيت إلى أن يتم إنشاء ذاكرة التخزين المؤقت assets
وتعبئتها بصورة سيف:
self.addEventListener("install", event => {
event.waitUntil(
caches.open("assets").then(cache => {
return cache.addAll(["/weapons/sword/blade.png"]);
})
);
});
احترِس من الوضع العالمي
وعندما يحدث هذا البدء/الإيقاف، تتم إعادة تعيين النطاق العمومي لعامل عامل الخدمة. لذا احذر من استخدام أي حالة عمومية في عامل الخدمات وإلا ستشعر بالحزن في المرة التالية التي يستعيد فيها الجهاز وتصبح حالته مختلفة عما كان متوقعًا.
ضع في الاعتبار هذا المثال الذي يستخدم حالة عمومية:
const favoriteNumber = Math.random();
let hasHandledARequest = false;
self.addEventListener("fetch", event => {
console.log(favoriteNumber);
console.log(hasHandledARequest);
hasHandledARequest = true;
});
عند كل طلب، سيسجِّل عامل الخدمة هذا رقمًا، لنفترض 0.13981866382421893
. يتغيّر المتغيّر hasHandledARequest
أيضًا إلى true
. الآن، يبقى عامل الخدمة غير نشِط لفترة قصيرة، لذا سيوقفه المتصفح. وفي المرة التالية التي يوجد فيها طلب، تكون هناك حاجة إلى عامل الخدمة مرة أخرى، حتى يوقظه المتصفح. يتم تقييم النص البرمجي مرة أخرى. والآن تمت إعادة ضبط hasHandledARequest
على false
، وأصبحت favoriteNumber
مختلفة تمامًا، 0.5907281835659033
.
لا يمكنك الاعتماد على الحالة المخزَّنة في مشغّل الخدمات. كذلك، قد يتسبب إنشاء مثيلات لأشياء مثل "قنوات الرسائل" في حدوث أخطاء: ستحصل على مثيل جديد تمامًا في كل مرة يتوقف فيها عامل الخدمة أو يبدأه.
في الفصل 3 من Service Workies، نظهِر عامل الخدمة المتوقّف عن العمل وكأنّه يفقد كل الألوان أثناء انتظاره للاستيقاظ.
معًا، ولكن منفصلة
لا يمكن التحكّم في صفحتك إلا من قِبل عامل خدمة واحد في كل مرة. ولكن يمكن أن يكون هناك عاملي خدمة مُثبّتين في آنٍ واحد. عند إجراء تغيير على رمز مشغّل الخدمات وإعادة تحميل الصفحة، لا يعني ذلك أنّك تعدّل عامل الخدمة على الإطلاق. عاملو الخدمة غير قابلين للتغيير. بدلاً من ذلك، أنت تنشئ واحدًا جديدًا تمامًا. سيتم تثبيت مشغّل الخدمات الجديد هذا (الذي نطلق عليه اسم SW2) ولكنه لن يتم تفعيله بعد. وعليك الانتظار حتى يتم إنهاء مشغّل الخدمات الحالي (SW1) (عندما يغادر المستخدم موقعك الإلكتروني).
العبث في ذاكرات التخزين المؤقت الخاصة بعامل خدمة آخر
أثناء التثبيت، يمكن أن تبدأ SW2 في إعداد الأشياء، وعادةً ما يتم إنشاء ذاكرات التخزين المؤقت وملؤها. لكن تنبيه: يمكن لمشغّل الخدمات الجديد هذا الوصول إلى كل البيانات التي يمكن لعامل الخدمات الحالي الوصول إليها. إذا لم تكن حذرًا، يمكن أن يفسد عامل خدمة الانتظار الجديد الأمور بالنسبة إلى عامل الخدمة الحالي. إليك بعض الأمثلة التي قد تسبب لك المشكلة:
- يمكن أن تحذف SW2 ذاكرة تخزين مؤقت تستخدمها بشكل نشط.
- قد تعدِّل SW2 محتوى ذاكرة التخزين المؤقت التي تستخدمها SW1، ما يؤدي إلى استجابة SW1 بمواد عرض لا تتوقعها الصفحة.
تخطي الانتظار
يمكن لمشغِّل الخدمات أيضًا استخدام طريقة skipWaiting()
الخطيرة للتحكّم في الصفحة فور انتهاء تثبيتها. وهذه فكرة سيئة بشكل عام إلا إذا كنت تحاول عمدًا استبدال عامل خدمة يتضمّن أعطالاً. قد يستخدم مشغّل الخدمات الجديد موارد محدَّثة لا تتوقعها الصفحة الحالية، ما يؤدي إلى حدوث أخطاء وأخطاء.
بدء التنظيف
إن طريقة منع موظفي الخدمة من اختراق بعضهم البعض هي التأكد من استخدامهم لذاكرة تخزين مؤقت مختلفة. وتتمثل أسهل طريقة لتنفيذ ذلك في نسخ أسماء ذاكرات التخزين المؤقت التي يستخدمونها.
const version = 1;
const assetCacheName = `assets-${version}`;
self.addEventListener("install", event => {
caches.open(assetCacheName).then(cache => {
// confidently do stuff with your very own cache
});
});
عندما تفعّل مشغّل خدمات جديد، سيؤدي هذا الإجراء إلى إيقاف تشغيل version
لتنفيذ ما يحتاج إليه من خلال ذاكرة تخزين مؤقت منفصلة تمامًا عن مشغّل الخدمات السابق.
إنهاء التنظيف
عندما يصل عامل الخدمات إلى حالة activated
، أنت تدرك أنّه تم تولي مسؤوليته، ومشغّل الخدمات السابق زائد عن الحاجة. في هذه المرحلة، من المهم التنظيف بعد عامل الخدمة القديم. فهو لا يحترم فقط احتياجات مستخدميك لحدود التخزين المؤقت في ذاكرة التخزين المؤقت، ولكن يمكنها أيضًا منع الأخطاء غير المقصودة.
الطريقة caches.match()
هي اختصار يُستخدم كثيرًا لاسترداد عنصر من أي ذاكرة تخزين مؤقت يحتوي على مطابقة فيها. لكنه يتكرر من خلال ذاكرات التخزين المؤقت بالترتيب الذي تم إنشاؤه به. لنفترض أن لديك نسختين من ملف النص البرمجي app.js
في ذاكرتين تخزينيتين مختلفتين: assets-1
وassets-2
. تنتظر صفحتك النص البرمجي الأحدث الذي تم تخزينه في assets-2
. في حال عدم حذف ذاكرة التخزين المؤقت القديمة، سيعرض caches.match('app.js')
النسخة القديمة من assets-1
ومن المرجح أن يؤدي إلى تعطُّل موقعك الإلكتروني.
كل ما يتطلبه الأمر للمسح بعد عاملي الخدمة السابقين هو حذف أي ذاكرة تخزين مؤقت لا يحتاج إليها عامل الخدمة الجديد:
const version = 2;
const assetCacheName = `assets-${version}`;
self.addEventListener("activate", event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== assetCacheName){
return caches.delete(cacheName);
}
});
);
});
);
});
يتطلب منع موظفي الخدمة من ارتباك بعضهم بعضًا من العمل والانضباط، ولكنه يستحق العناء.
طريقة تفكير العاملين في الخدمات
سيساعدك اكتساب التفكير الصحيح أثناء التفكير في عاملي الخدمة في بناء قدراتك بثقة. وبعد التعرف عليها، ستتمكن من تقديم تجارب مذهلة للمستخدمين.
أنت محظوظ إذا أردت فهم كل هذا من خلال لعب لعبة. انتقل إلى Service Workies حيث ستتعرف على طرق عامل الخدمة للقضاء على وحوش العمل غير المتصلة بالإنترنت.