التحميل الفوري لتطبيقات الويب باستخدام بنية هيكل التطبيق

صدفة التطبيق هي الحد الأدنى من HTML وCSS وJavaScript التي تشغّل واجهة المستخدم. يجب أن يستوفي هيكل التطبيق الشروط التالية:

  • تحميل سريع
  • أن تكون مخزّنة مؤقتًا
  • عرض المحتوى ديناميكيًا

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

فصل محتوى HTML عن محتوى JavaScript وCSS ومحتوى HTML في ملف App Shell

الخلفية

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

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

لنلقِ نظرة على كيفية تنظيم تطبيقك باستخدام بنية ملف تعريف shell المعزّزة بمشغّل الخدمة. سنلقي نظرة على العرض من جهة العميل والخادم، وسنشارك عيّنة شاملة يمكنك تجربتها اليوم.

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

صورة لخادم الخدمة الذي يعمل في "أدوات مطوّري البرامج" لحِزمة تطبيق

ما هي مشغّلو الخدمات؟

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

تتوفّر أيضًا لواجهات برمجة التطبيقات Service Worker مجموعة محدودة من واجهات برمجة التطبيقات مقارنةً بلغة JavaScript في سياق التصفّح العادي. وهذا هو المعيار المتّبع لالموظفين على الويب. لا يمكن لعامل الخدمة الوصول إلى DOM، ولكن يمكنه الوصول إلى عناصر مثل Cache API، ويمكنه إرسال طلبات إلى الشبكة باستخدام Fetch API. تتوفّر أيضًا واجهة برمجة التطبيقات IndexedDB API وpostMessage() لاستخدامهما في الاحتفاظ بالبيانات والمراسلة بين الخدمة العاملة والصفحات التي تتحكّم فيها. يمكن أن تُستخدَم أحداث الإشعارات الفورية المُرسَلة من خادمك لطلب Notification API لزيادة تفاعل المستخدمين.

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

للاطّلاع على مزيد من المعلومات عن مشغِّلات الخدمات بالتفصيل، يمكنك قراءة مقدّمة عن مشغِّلات الخدمات.

مزايا الأداء

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

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

لاختبار هذه البنية على الأجهزة الحقيقية، أجرينا نموذج ملفّ أذونات التطبيق على WebPageTest.org وعرضنا النتائج أدناه.

الاختبار 1: الاختبار باستخدام كابل مع هاتف Nexus 5 باستخدام إصدار Chrome المخصّص للمطوّرين

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

رسم بياني لاختبار رسم صفحة الويب لتوصيل الكابل

الاختبار 2: الاختبار على شبكة الجيل الثالث باستخدام هاتف Nexus 5 باستخدام إصدار Chrome المخصّص للمطوّرين

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

رسم بياني لاختبار رسم صفحة الويب للاتصال بشبكة الجيل الثالث

تروي وجهات النظر الأخرى قصة مشابهة. قارِن الثواني الثلاث التي يستغرقها تطبيقك لعرض أوّل محتوى مفيد في واجهة التطبيق:

المخطط الزمني لعرض أول مشاهدة من اختبار صفحة الويب

إلى 0.9 ثانية التي يستغرقها تحميل الصفحة نفسها من ذاكرة التخزين المؤقت لمشغّل الخدمة. يتم توفير أكثر من ثانيتَين من الوقت للمستخدمين النهائيين.

رسم مخطط زمني لمشاهدة الإعلان المتكرّرة من اختبار صفحة الويب

يمكنك تحقيق أداء مشابه وموثوق به لتطبيقاتك باستخدام بنية ملف أذونات التطبيق.

هل تتطلّب تقنية "خدمة عامل الخدمة" إعادة التفكير في طريقة تنظيم التطبيقات؟

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

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

ماذا عن التحسين التدريجي؟

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

يمكنك أدناه الاطّلاع على النسخة الكاملة المعروضة في Chrome وFirefox Nightly وSafari. على يمين الصفحة، يمكنك الاطّلاع على إصدار Safari الذي يتم فيه عرض المحتوى على الخادم بدون عامل خدمة. على يسار الصفحة، نرى إصدارَي Chrome وFirefox Nightly اللذَين يعملان باستخدام الخدمة العاملة.

صورة لـ Application Shell محمَّلة في Safari وChrome وFirefox

متى يكون من المنطقي استخدام هذه البنية؟

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

هل هناك أي تطبيقات قيد الاستخدام تستخدم هذا النمط حتى الآن؟

يمكن إنشاء بنية واجهة التطبيق من خلال إجراء بعض التغييرات على واجهة المستخدم العامة للتطبيق، وقد أثبتت هذه الطريقة فعاليتها في المواقع الإلكترونية الكبيرة، مثل تطبيق الويب المتقدّم I/O 2015 من Google و"البريد الوارد" من Google.

صورة لتحميل البريد الوارد في Google يوضّح البريد الوارد باستخدام الخدمة العاملة.

تُحقّق تطبيقات التطبيقات بلا إنترنت أداءً ممتازًا، كما يمكن الاطّلاع عليها في تطبيق Wikipedia بلا إنترنت الذي أنشأه "جاك أرشيبالد" وتطبيق الويب المتقدّم Flipkart Lite.

لقطات شاشة لعرض "ويكيبيديا" التجريبي الذي أعدّه "جاك أرشيبالد"

شرح البنية

خلال تجربة التحميل الأولى، يكون هدفك هو عرض محتوى مفيد على شاشة المستخدم في أسرع وقت ممكن.

التحميل الأول وتحميل الصفحات الأخرى

مخطّط بياني لعملية التحميل الأولى باستخدام ملف App Shell

بشكل عام، ستتضمّن بنية هيكل التطبيق ما يلي:

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

  • يتم تحميل كل المحتوى الآخر بشكل بطيء أو في الخلفية. ومن الخيارات الجيدة استخدام ذاكرة التخزين المؤقت للقراءة للمحتوى الديناميكي.

  • استخدِم أدوات موظّف الخدمة، مثل sw-precache، على سبيل المثال، لتخزين موظّف الخدمة الذي يدير المحتوى الثابت وتعديله بشكل موثوق. (مزيد من المعلومات حول sw-precache لاحقًا)

لتحقيق ذلك:

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

  • ستتضمّن الصفحات أنماط CSS مضمّنة في علامة <style> ضمن المستند <head> لتوفير عرض أوّل سريع لكُمّة التطبيق. ستحمِّل كل صفحة JavaScript بشكل غير متزامن لعرض الصفحة الحالية. بما أنّه لا يمكن تحميل CSS بشكل غير متزامن، يمكننا طلب الأنماط باستخدام JavaScript لأنّه غير متزامن بدلاً من أن يكون مستندًا إلى التحليل ومتزامنًا. يمكننا أيضًا الاستفادة من requestAnimationFrame() لتجنّب الحالات التي قد نحصل فيها على نتيجة سريعة من ذاكرة التخزين المؤقت وتصبح الأنماط جزءًا من مسار المعالجة الحرج عن طريق الخطأ. تفرض requestAnimationFrame() رسم الإطار الأول قبل تحميل الأنماط. هناك خيار آخر وهو استخدام مشاريع مثل loadCSS من Filament Group لطلب CSS بشكل غير متزامن باستخدام JavaScript.

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

واجهة التطبيق للمحتوى

تطبيق عملي

لقد كتبنا نموذجًا يعمل بشكل كامل باستخدام بنية صدفة التطبيق ولغة JavaScript ES2015 العادية للعميل وExpress.js للخادم. لا يوجد بالطبع ما يمنعك من استخدام حِزمة البرامج الخاصة بك لأي من أجزاء العميل أو الخادم (مثل PHP أو Ruby أو Python).

مراحل مشغّل الخدمات

في مشروع ملفّ أذونات التطبيق، نستخدم sw-precache الذي يقدّم دورة حياة عامل الخدمة التالية:

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

أجزاء الخادم

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

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

مخطّط بياني لبنية &quot;هيكل التطبيق&quot;
  • يتمّ تحديد نقاط النهاية لثلاثة أجزاء من تطبيقك: عناوين URL الموجّهة للمستخدمين (الفهرس/العلامة ال wildcard) وقشرة التطبيق (عامل الخدمة) والأجزاء الجزئية من صفحات HTML.

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

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

  • ستؤدي بنية التطبيق بعد ذلك دور تطبيق ويب مكوّن من صفحة واحدة، باستخدام JavaScript لطلب البيانات عبر بروتوكول XHR في المحتوى لعنوان URL محدّد. يتم إجراء طلبات XHR إلى نقطة نهاية ‎ /partials* التي تعرض جزءًا صغيرًا من HTML وCSS وJS المطلوب لعرض هذا المحتوى. ملاحظة: هناك العديد من الطرق لتنفيذ ذلك، وتعدّ طلبات البيانات عبر HTTP (XHR) إحدى هذه الطرق. ستضمّن بعض التطبيقات بياناتها (ربما باستخدام JSON) للعرض الأوّلي، وبالتالي لن تكون "ثابتة" في سياق HTML المسطّح.

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

تحديد إصدارات الملفات

يطرح سؤال حول كيفية التعامل مع إصدارات الملفات وتحديثها. يعتمد ذلك على التطبيق، والخيارات هي:

  • يتم استخدام الشبكة أولاً، وفي حال عدم توفّر شبكة، يتم استخدام النسخة المخزّنة مؤقتًا.

  • الشبكة فقط، ولا تعمل في حال عدم الاتصال بالإنترنت

  • تخزين الإصدار القديم في ذاكرة التخزين المؤقت وتحديثه لاحقًا

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

الأدوات

نحافظ على عدد من مكتبات مساعدة مهام الخدمة المختلفة التي تسهّل عملية التخزين المؤقت المُسبَق لغلاف تطبيقك أو معالجة أنماط التخزين المؤقت الشائعة.

لقطة شاشة لموقع مكتبة Service Worker على Web Fundamentals

استخدام sw-precache لصدفة تطبيقك

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

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

استخدام sw-toolbox لتخزين البيانات مؤقتًا أثناء التشغيل

استخدِم sw-toolbox لتخزين المحتوى مؤقتًا أثناء التشغيل باستخدام استراتيجيات مختلفة حسب المورد:

  • cacheFirst للصور، بالإضافة إلى ذاكرة تخزين مؤقت مخصّصة مُسمّاة لها سياسة انتهاء صلاحية مخصّصة تبلغ N maxEntries

  • networkFirst أو fastest لطلبات البيانات من واجهة برمجة التطبيقات، استنادًا إلى مدى حداثة المحتوى المطلوب قد يكون الخيار الأسرع مناسبًا، ولكن إذا كانت هناك خلاصة واجهة برمجة تطبيقات معيّنة يتم تعديلها بشكل متكرّر، استخدِم الخيار networkFirst.

الخاتمة

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

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

إذا كنت تفكر في استخدام مشغّلات الخدمات في تطبيقك، اطّلِع على البنية وتقييم ما إذا كان ذلك مناسبًا لمشاريعك.

مع أطيب التحيّات، مراجعو المحتوى: جيف بوسنيك، وبول لويس، وأليكس راسل، وساتث تومسون، وروب دوسن، وتايلور سافاج، وجوي ميدلي.