كيفية التفكير في عمال الخدمات
إنّ خدمات العملاء فعّالة جدًا وتستحق التعلم. تتيح لك هذه التجارب تقديم مستوى جديد تمامًا من التجارب للمستخدمين. يجب أن يتم تحميل موقعك الإلكتروني على الفور. يمكن استخدامها بلا إنترنت. ويمكن تثبيته كتطبيق خاص بمنصّة معيّنة، ويكون على مستوى عالٍ من الجودة، ولكن مع ميزة الوصول إلى الجمهور وحرية الويب.
ولكنّ مهام الخدمة تختلف عن أيّ شيء اعتاد عليه معظم مطوّري الويب. وتتطلب هذه الأدوات فترة تعلم طويلة وبعض الخطوات التي يجب الانتباه إليها.
تعاونتُ مؤخرًا مع فريق Google Developers في مشروع Service Workies، وهي لعبة مجانية لفهم مشغّلات الخدمات. أثناء إنشاء التطبيق والعمل على التفاصيل المعقدة لخدمة workers، واجهت بعض المشاكل. ما ساعدني كثيرًا هو ابتكار عدد من الاستعارات التصويرية. في هذه المشاركة، سنستكشف هذه النماذج الذهنية ونحاول فهم السمات المتناقضة التي تجعل موظفي الخدمات صعبين ومذهلين في الوقت نفسه.
الإجراء نفسه، ولكن بشكل مختلف
أثناء كتابة رمز عامل الخدمة، ستلاحظ أنّ العديد من العناصر مألوفة لك. يمكنك استخدام ميزات لغة JavaScript الجديدة المفضّلة لديك. يمكنك الاستماع إلى أحداث دورة النشاط تمامًا كما هو الحال مع أحداث واجهة المستخدم. يمكنك إدارة تدفّق التحكّم باستخدام الوعود كما اعتدت.
ولكن سلوك عامل الخدمة الآخر يثير حيرتك. خاصةً عند إعادة تحميل الصفحة وعدم رؤية التغييرات التي أجريتها على الرمز.
طبقة جديدة
عند إنشاء موقع إلكتروني، عادةً ما يكون لديك طبقتان فقط للتفكير فيهما: العميل والخادم. ويكون "عامل الخدمة" طبقة جديدة تمامًا في الوسط.
يمكنك اعتبار مشغّل الخدمات نوعًا من إضافات المتصفّح التي يمكن لموقعك الإلكتروني تثبيتها في متصفّح المستخدم. بعد التثبيت، يُوسّع موظّف الخدمة نطاق المتصفح لموقعك الإلكتروني من خلال طبقة وسطى فعّالة. يمكن أن تعترض طبقة الخدمة هذه جميع الطلبات التي يقدّمها موقعك الإلكتروني وتعالجها.
تتضمّن طبقة الخدمة دورة حياة خاصة بها مستقلة عن علامة التبويب في المتصفّح. لا تكفي إعادة تحميل الصفحة البسيطة لتعديل عامل الخدمة، تمامًا كما لا تتوقّع أن تؤدي إعادة تحميل الصفحة إلى تعديل الرمز البرمجي الذي تم نشره على خادم. ولكل طبقة قواعدها الفريدة الخاصة بها لتعديلها.
في لعبة Service Workies، نتناول العديد من التفاصيل حول دورة حياة الخدمة وسنقدّم لك الكثير من التمارين العملية لاستخدامها.
فعّالة، ولكنّها محدودة
يمنحك استخدام مشغّل خدمات على موقعك الإلكتروني مزايا رائعة. يمكن لموقعك الإلكتروني إجراء ما يلي:
- العمل بسلاسة حتى في حال عدم اتصال المستخدم بالإنترنت
- تحقيق تحسينات كبيرة في الأداء من خلال التخزين المؤقت
- استخدام الإشعارات الفورية
- أن يتم تثبيته كتطبيق PWA
على الرغم من الإجراءات الكثيرة التي يمكن أن يتّخذها موظّفو الخدمة، إلا أنّهم محدودون من حيث التصميم. ولا يمكنه تنفيذ أي إجراء متزامن أو في سلسلة المحادثات نفسها التي يستخدمها موقعك الإلكتروني. وهذا يعني عدم إمكانية الوصول إلى ما يلي:
- localStorage
- نموذج عناصر المستند
- النافذة
والخبر السارّ هو أنّ هناك عددًا من الطرق التي يمكن لصفحتك من خلالها التواصل مع عامل الخدمة، بما في ذلك postMessage
المباشر وقنوات الرسائل بين شخصَين وقنوات البث بين شخص والعديد من الأشخاص.
محتوى يستمرّ لفترة طويلة، ولكنّه قصير
يستمر تشغيل Worker نشط في الخدمة حتى بعد مغادرة المستخدِم لموقعك الإلكتروني أو إغلاق علامة التبويب. يحافظ المتصفّح على مشغّل الخدمة هذا لكي يكون جاهزًا في المرة التالية التي يعود فيها المستخدم إلى موقعك الإلكتروني. قبل تقديم الطلب الأول، يحصل مشغّل الخدمة على فرصة اعتراضه والتحكم في الصفحة. وهذا ما يسمح لموقع إلكتروني بالعمل بلا اتصال بالإنترنت، إذ يمكن لمشغّل الخدمة عرض نسخة مخزّنة مؤقتًا من الصفحة نفسها، حتى إذا لم يكن لدى المستخدم اتصال بالإنترنت.
في Service Workies، نوضّح هذا المفهوم من خلال شخصية "كولو" (عامل خدمة ودود) الذي يعترض الطلبات ويعالجها.
متوقفة
على الرغم من أنّ عمال الخدمة يبدون خالدين، يمكن إيقافهم في أي وقت تقريبًا. لا يريد المتصفّح إهدار الموارد على مشغّل خدمات لا ينفّذ أيّ مهمة حاليًا. يختلف إيقاف العامل عن إنهاء عمله، إذ يظلّ العامل مثبّتًا ومفعّلاً. تم وضعه في وضع السكون. وفي المرة التالية التي يكون فيها ذلك ضروريًا (مثلاً، لمعالجة طلب)، يوقِظ المتصفّح المعالج من جديد.
waitUntil
بسبب احتمالية إيقافه باستمرار، يحتاج العامل في الخدمة إلى طريقة لإعلام المتصفّح عندما يكون في وضع تنفيذ مهمة مهمة ولا يريد أخذ قيلولة. هنا يأتي دور event.waitUntil()
. وتؤدي هذه الطريقة إلى إطالة دورة الحياة التي يتم استخدامها فيها، ما يمنع إيقافها والانتقال إلى المرحلة التالية من دورة حياتها إلى أن نصبح جاهزين. يمنحنا ذلك الوقت اللازم لإعداد ذاكرات التخزين المؤقت، وجلب الموارد من الشبكة، وما إلى ذلك.
يُعلم هذا المثال المتصفّح بأنّه لم تكتمل عملية تثبيت الخدمة إلى أن يتم إنشاء ذاكرة التخزين المؤقت assets
وملؤها بصورة سيف:
self.addEventListener("install", event => {
event.waitUntil(
caches.open("assets").then(cache => {
return cache.addAll(["/weapons/sword/blade.png"]);
})
);
});
انتبه إلى الحالة الشاملة
عند حدوث عملية البدء/الإيقاف هذه، تتم إعادة ضبط النطاق الشامل لعامل الخدمة. لذا، احرِص على عدم استخدام أي حالة عامة في worker الخدمة، وإلا ستصاب بخيبة أمل في المرة التالية التي يتم فيها إعادة تشغيله وتكون حالته مختلفة عن الحالة المتوقّعة.
إليك مثال يستخدم حالة عامة:
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 من "مشغّلو الخدمات"، نعرض مشغّل الخدمة المتوقف على أنّه يفقد كل الألوان أثناء انتظار إعادة تشغيله.
معًا، ولكن بشكل منفصل
لا يمكن التحكّم في صفحتك إلا من خلال مشغّل خدمات واحد في كل مرة. ولكن يمكن أن يكون لديه عاملان للخدمة مثبَّتان في الوقت نفسه. عند إجراء تغيير على رمز عامل الخدمة وإعادة تحميل الصفحة، لا يتم تعديل عامل الخدمة على الإطلاق. ملفات تشغيل الخدمات غير قابلة للتغيير. بدلاً من ذلك، يتم إنشاء حساب جديد تمامًا. سيتم تثبيت عامل الخدمة الجديد هذا (لنسمّيه SW2) ولكن لن يتم تفعيله بعد. يجب أن wait حتى يتم إنهاء عامل الخدمة الحالي (SW1) (عندما يغادر المستخدم موقعك الإلكتروني).
العبث بمخازن مؤقتة لمشغّل خدمة آخر
أثناء التثبيت، يمكن أن يبدأ SW2 عملية الإعداد، والتي تتضمن عادةً إنشاء ذاكرات التخزين المؤقت وملؤها. يُرجى العِلم أنّ عامل الخدمة الجديد هذا يمكنه الوصول إلى كل ما يمكن لعامل الخدمة الحالي الوصول إليه. إذا لم تكن حذرًا، يمكن أن يؤدي مشغّل الخدمة الجديد في وضع الانتظار إلى إفساد الأمور لمشغّل الخدمة الحالي. في ما يلي بعض الأمثلة التي قد تؤدي إلى حدوث مشاكل:
- يمكن أن يحذف SW2 ذاكرة التخزين المؤقت التي يستخدمها SW1 بشكل نشط.
- يمكن أن يعدّل SW2 محتوى ذاكرة التخزين المؤقت التي يستخدمها SW1، ما يؤدي إلى أن يردّ SW1 بمواد عرض لا تتوقعها الصفحة.
تخطّي skipWaiting
يمكن لمشغّل الخدمات أيضًا استخدام طريقة 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 للتعرّف على طرق استخدام مهام الخدمة من أجل القضاء على الوحوش التي تعمل بلا إنترنت.