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

آدي عثمانية
آدي عثمانية

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

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

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

فصل هيكل التطبيق بين هيكل HTML وJS وCSS ومحتوى HTML

الخلفية

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

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

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

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

صورة مشغّل الخدمات قيد التشغيل في "أدوات مطوري البرامج" لهيكل التطبيق

مرّة أخرى، ما المقصود بعاملي الخدمة؟

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

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

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

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

مزايا الأداء

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

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

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

الاختبار الأول: اختبار الكابل باستخدام جهاز Nexus 5 باستخدام إصدار مطوّري البرامج من Chrome

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

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

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

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

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

هناك مشاهد أخرى تروي قصة مماثلة. قارن بين 3 ثوانٍ المستغرقة للوصول إلى أول طلاء مفيد في واجهة التطبيق:

نسخ المخطط الزمني للعرض الأول من "اختبار صفحة الويب"

إلى 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 في تطبيق الويب التقدمي.

لقطات شاشة لمحتوى Wikipedia حول "جيك أرشيبالد"

شرح البنية

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

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

رسم تخطيطي للتحميل الأول باستخدام App Shell

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

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

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

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

لتحقيق ذلك:

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

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

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

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

التنفيذ العملي

لقد كتبنا نموذجًا يعمل بكامل طاقته باستخدام بنية هيكل التطبيق، وJavaScript vanilla ES2015 للعميل، وExpress.js للخادم. ولا يوجد ما يمنعك بالطبع من استخدام مكدسك الخاص إما للعميل أو لأجزاء الخادم (على سبيل المثال PHP أو Ruby أو Python).

مراحل نشاط عامل الخدمات

بالنسبة إلى مشروع هيكل التطبيق، نستخدم دالة sw-precache التي تقدّم مراحل نشاط مشغّل الخدمات التالية:

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

وحدة بت الخادم

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

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

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

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

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

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

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

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

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

  • يجب الاتصال بالشبكة أولاً واستخدام النسخة المخبأة.

  • الشبكة فقط وتتعذّر الاتصال إذا كانت غير متصلة بالإنترنت.

  • تخزين النسخة القديمة مؤقتًا والتحديث لاحقًا.

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

أدوات

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

لقطة شاشة لموقع Service Worker Library Site على Web Fundamentals

استخدام sw-precache للتعامل مع هيكل التطبيق

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

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

استخدام sw-toolbox للتخزين المؤقت في وقت التشغيل

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

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

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

الخاتمة

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

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

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

بفضل المراجعين: "جيف بوسنيك" و"بول لويس" و"أليكس راسل" و"سيث تومسون" و"روب دودسون" و"تايلور سافاج" و"جو ميدلي".