هيكل التطبيق هو الحد الأدنى من رموز HTML وCSS وJavaScript التي تدعم واجهة المستخدم. في ما يلي المتطلبات التي يجب أن يستوفيها هيكل التطبيق:
- تحميل سريع
- مخزَّن مؤقتًا
- عرض المحتوى ديناميكيًا
هيكل التطبيق هو السر في الأداء الجيد الموثوق. لنفترض أنّ هيكل التطبيق يشبه حزمة الرموز التي ستنشرها في متجر التطبيقات إذا كنت تنشئ تطبيقًا محليًا. فهو الحمل المطلوب للانطلاق، ولكنه قد لا يكون القصة الكاملة. وهو يحافظ على واجهة المستخدم محليًا ويسحب المحتوى ديناميكيًا من خلال واجهة برمجة التطبيقات.
![فصل هيكل التطبيق بين HTML وJS وCSS Shell ومحتوى HTML](https://developer.chrome.google.cn/static/blog/app-shell/image/app-shell-separation-htm-4003c743cac91.jpg?authuser=9&hl=ar)
الخلفية
توضّح مقالة تطبيقات الويب التقدّمية التي أنشأها "أليكس راسل" كيف يمكن لتطبيق الويب أن يتغيّر تدريجيًا من خلال الموافقة على الاستخدام وموافقة المستخدم لتقديم تجربة مشابهة للتطبيق الأصلي مع توفير الدعم بلا اتصال بالإنترنت والإشعارات الفورية وإمكانية الإضافة إلى الشاشة الرئيسية. ويعتمد ذلك إلى حد كبير على الوظائف ومزايا الأداء التي يتمتع بها عاملو الخدمات وقدراتهم في التخزين المؤقت. ويتيح لك ذلك التركيز على السرعة، ما يمنح تطبيقات الويب التحميل الفوري نفسه والتحديثات المنتظمة التي اعتدت الاطّلاع عليها في التطبيقات الأصلية.
وللاستفادة بشكل كامل من هذه الإمكانيات، نحتاج إلى طريقة جديدة للتفكير في المواقع الإلكترونية: بنية هيكل التطبيق.
لنطّلِع بالتفصيل على طريقة تصميم بنية تطبيقك باستخدام بنية هيكل التطبيق المعززة لمشغّل الخدمات. سنلقي نظرة على العرض من جهة العميل والخادم ونشارك عيّنة من هذه النماذج يمكنك تجربتها الآن.
للتأكيد على هذه النقطة، يوضح المثال أدناه التحميل الأول لتطبيق باستخدام هذه البنية. لاحظ الإشعار المنبثق "التطبيق جاهز للاستخدام بلا اتصال بالإنترنت" في أسفل الشاشة. إذا أصبح تحديث واجهة المستخدم متوفّرًا في وقت لاحق، يمكننا إبلاغ المستخدم بإعادة التحميل لاستخدام الإصدار الجديد.
![صورة عامل خدمات يعمل في "أدوات مطوري البرامج" لهيكل التطبيق](https://developer.chrome.google.cn/static/blog/app-shell/image/image-service-worker-run-02f2264b236e.png?authuser=9&hl=ar)
مرة أخرى، من هم عاملو الخدمات؟
عامل الخدمة هو نص برمجي يتم تشغيله في الخلفية ومنفصل عن صفحة الويب. وهو يستجيب للأحداث، بما في ذلك طلبات الشبكة من الصفحات التي يعرضها وإرسال الإشعارات من الخادم. لنفترض أن عمر عامل الخدمة قصير عمدًا. حيث يستيقظ عند تلقي حدث وتشغيله فقط طالما أنه بحاجة إلى معالجته.
يمتلك مشغّلو الخدمات أيضًا مجموعة محدودة من واجهات برمجة التطبيقات مقارنةً بلغة JavaScript في سياق التصفّح العادي. وهذا أمر عادي للعاملين على الويب. لا يمكن لمشغّل الخدمات الوصول إلى نموذج العناصر في المستند (DOM)، ولكن يمكنه الوصول إلى أشياء مثل واجهة برمجة تطبيقات ذاكرة التخزين المؤقت، ويمكنه إجراء طلبات الشبكة باستخدام واجهة برمجة تطبيقات الجلب. تتوفر أيضًا IndexedDB API وpostMessage() لاستخدامهما لتثبيت البيانات والمراسلة بين مشغّل الخدمة والصفحات التي يتحكّم فيها. يمكن للأحداث الفورية المُرسَلة من خادمك استدعاء Notification API لزيادة تفاعل المستخدمين.
يمكن عامل الخدمة اعتراض طلبات الشبكة التي يتم إجراؤها من إحدى الصفحات (مما يؤدي إلى تشغيل حدث جلب على مشغّل الخدمات) وعرض رد تم استرداده من الشبكة أو استرداده من ذاكرة تخزين مؤقت محلية أو حتى يتم إنشاؤه بطريقة آلية. وهو من الناحية الفعّالة خادم وكيل قابل للبرمجة في المتصفح. الأمر المتقن هو أنّه بغض النظر عن مصدر الردّ، تطّلع على صفحة الويب كما لو لم يكن هناك أي مشاركة لمشغّلي الخدمات.
للمزيد من المعلومات التفصيلية عن مشغّلي الخدمات، يُرجى الاطّلاع على مقالة مقدّمة حول مشغِّلي الخدمات.
مزايا الأداء
يتميّز مشغّلو الخدمات بفعّالية في التخزين المؤقت بلا إنترنت، ولكنّهم يقدّمون أيضًا مكاسب كبيرة في الأداء من خلال التحميل الفوري للزيارات المتكرّرة إلى موقعك الإلكتروني أو تطبيق الويب. ويمكنك تخزين هيكل التطبيق في ذاكرة التخزين المؤقت كي يعمل بلا اتصال بالإنترنت وتعبئة محتواه باستخدام JavaScript.
عند تكرار الزيارات، يتيح لك ذلك عرض وحدات بكسل مفيدة على الشاشة بدون الشبكة، حتى إذا صدر المحتوى منها في النهاية. يمكنك اعتبار هذه الميزة بمثابة عرض أشرطة الأدوات والبطاقات على الفور، ثم تحميل باقي المحتوى تدريجيًا.
لاختبار هذه البنية على أجهزة حقيقية، أجرينا نموذج هيكل التطبيق على WebPageTest.org وعرضنا النتائج أدناه.
الاختبار 1: الاختبار على الكابل باستخدام جهاز Nexus 5 باستخدام إصدار مطوّري البرامج من Chrome
يجب أن يجلب العرض الأول للتطبيق جميع الموارد من الشبكة ولا يتم تحقيق عرض مفيد إلا بعد 1.2 ثانية. بفضل التخزين المؤقت للعاملين في الخدمات، تقدّم الزيارة المتكررة سرعة عرض محتوى الصفحة وتنتهي من التحميل بالكامل خلال 0.5 ثانية.
![رسم تخطيطي لاختبار صفحة الويب لاتصال الكابل](https://developer.chrome.google.cn/static/blog/app-shell/image/web-page-test-paint-diagr-41aa56b625ee3.png?authuser=9&hl=ar)
الاختبار 2: الاختبار على شبكة الجيل الثالث باستخدام جهاز Nexus 5 باستخدام إصدار مطوّري البرامج من Chrome
يمكننا أيضًا اختبار النموذج باستخدام اتصال شبكة 3G أبطأ قليلاً. تستغرق هذه المرة 2.5 ثانية عند الزيارة الأولى لعرض أول محتوى مهم. يستغرق تحميل الصفحة بالكامل 7.1 ثانية. من خلال التخزين المؤقت لمشغّل الخدمات، تحقّق زياراتنا المتكرّرة سرعة عرض الصفحة وتنتهي من التحميل بالكامل خلال 0.8 ثانية.
![رسم تخطيطي لاختبار صفحة الويب لاتصال شبكة الجيل الثالث](https://developer.chrome.google.cn/static/blog/app-shell/image/web-page-test-paint-diagr-b6adad69ab243.png?authuser=9&hl=ar)
تروي مشاهدات أخرى قصة مشابهة. يمكنك مقارنة الـ 3 ثوانٍ المُستغرَقة للوصول إلى سرعة عرض أول محتوى مفيد في واجهة هيكل التطبيق:
![عرض المخطط الزمني لتصوير العرض الأول من اختبار صفحة الويب](https://developer.chrome.google.cn/static/blog/app-shell/image/paint-timeline-first-vie-699786d3030cf.png?authuser=9&hl=ar)
إلى 0.9 ثانية التي تستغرقها عند تحميل الصفحة نفسها من ذاكرة التخزين المؤقت لعامل الخدمة. يتم توفير أكثر من ثانيتين من الوقت للمستخدمين النهائيين.
![عرض المخطط الزمني لتكرار العرض من اختبار صفحة الويب](https://developer.chrome.google.cn/static/blog/app-shell/image/paint-timeline-repeat-vi-1e38f56ee31be.png?authuser=9&hl=ar)
ومن الممكن تحقيق مكاسب أداء مماثلة وموثوقة لتطبيقاتك باستخدام بنية هيكل التطبيق.
هل يتطلّب منا مشغّل الخدمات إعادة التفكير في طريقة هيكلة التطبيقات؟
يشير عاملو الخدمة إلى بعض التغييرات الطفيفة في بنية التطبيقات. بدلاً من ضغط كل تطبيقك في سلسلة HTML، قد يكون من المفيد تنفيذ إجراءات بأسلوب AJAX. في هذه الحالة، يكون لديك واجهة أوامر (يتم تخزينها دائمًا في ذاكرة التخزين المؤقت ويمكن تشغيلها في أي وقت بدون الاتصال بالشبكة) ومحتوى يتمّ تحديثه بانتظام ويُدار بشكل منفصل.
إنّ الآثار المترتبة على هذا التقسيم كبيرة. في الزيارة الأولى، يمكنك عرض المحتوى على الخادم وتثبيت عامل الخدمة على الجهاز العميل. وفي الزيارات اللاحقة، تحتاج فقط إلى بيانات الطلب.
ماذا عن التحسين التدريجي؟
على الرغم من أنّ جميع المتصفحات لا تتوافق مع مشغّل الخدمات في الوقت الحالي، تستخدم بنية هيكل محتوى التطبيق التحسينات التدريجي لضمان وصول الجميع إلى المحتوى. على سبيل المثال، خذ نموذج المشروع الخاص بنا.
يمكنك الاطّلاع أدناه على النسخة الكاملة المعروضة في Chrome وFirefox Nightly وSafari. على أقصى اليمين، يمكنك مشاهدة إصدار Safari حيث يُعرَض المحتوى على الخادم بدون مشغّل خدمات. على اليسار، نرى إصدارَي Chrome وFirefox Nightly الذي يدعمه مشغّل الخدمات.
![صورة لـ Application Shell تم تحميلها في Safari وChrome وFirefox](https://developer.chrome.google.cn/static/blog/app-shell/image/image-application-shell-9836a3aa49a09.jpg?authuser=9&hl=ar)
متى يكون من المنطقي استخدام هذه البنية؟
تكون بنية هيكل التطبيق أكثر منطقية للتطبيقات والمواقع الديناميكية. إذا كان موقعك الإلكتروني صغيرًا وثابتًا، لا تحتاج على الأرجح إلى هيكل تطبيق، ويمكنك ببساطة تخزين الموقع الإلكتروني بأكمله في ذاكرة التخزين المؤقت في خطوة oninstall
عامل الخدمات. استخدم النهج الأكثر ملاءمةً لمشروعك. يشجع عدد من أطر عمل JavaScript بالفعل على تقسيم منطق تطبيقك عن المحتوى، مما يجعل هذا النمط أكثر سهولة في التطبيق.
هل هناك أي تطبيقات إنتاج تستخدم هذا النمط حتى الآن؟
تكون بنية هيكل التطبيق ممكنة من خلال إجراء بعض التغييرات فقط على واجهة مستخدم التطبيق بشكل عام، وقد أفادت هذه البنية بشكل جيد المواقع الإلكترونية الواسعة النطاق، مثل تطبيق الويب التقدّمي I/O 2015 من Google والبريد الوارد في Google.
![صورة "بريد Google الوارد" قيد التحميل صورة توضيحية للبريد الوارد باستخدام عامل الخدمة](https://developer.chrome.google.cn/static/blog/app-shell/image/image-google-inbox-loadi-7add11408f9e4.png?authuser=9&hl=ar)
حققت واجهات التطبيقات غير المتصلة بالإنترنت مكاسب كبيرة في الأداء، كما تظهر بشكلٍ جيد في تطبيق ويكيبيديا بلا إنترنت من Jake Aribald وتطبيق Flipkart Lite على الويب التقدّمي.
![لقطات شاشة للعرض التوضيحي لجيك أرشيبالد على Wikipedia](https://developer.chrome.google.cn/static/blog/app-shell/image/screenshots-jake-archiba-9ac3ab9fd6213.jpg?authuser=9&hl=ar)
شرح الهندسة
أثناء تجربة التحميل الأولى، يكون هدفك هو توصيل محتوى ذي معنى إلى شاشة المستخدم بأسرع وقت ممكن.
يجب أولاً تحميل الصفحات الأخرى وتحميلها
![مخطّط بياني لعملية التحميل الأولى باستخدام هيكل التطبيق](https://developer.chrome.google.cn/static/blog/app-shell/image/diagram-the-load-the-a-b270f64ec820d.png?authuser=9&hl=ar)
بوجه عام، ستتخذ بنية هيكل التطبيق ما يلي:
إعطاء الأولوية للتحميل الأولي، ولكن مع السماح لعامل الخدمة بتخزين هيكل التطبيق في ذاكرة التخزين المؤقت حتى لا تتطلب الزيارات المتكررة إعادة جلب واجهة المستخدم من الشبكة.
التحميل الكسول أو تحميل المحتوى في الخلفية وغير ذلك ويتمثل أحد الخيارات الجيدة في استخدام التخزين المؤقت للقراءة للمحتوى الديناميكي.
استخدام أدوات مشغّل الخدمات، مثل sw-precache، على سبيل المثال، لتخزين مشغِّل الخدمات الذي يدير المحتوى الثابت وتعديله بشكل موثوق. (يمكنك الاطّلاع على مزيد من المعلومات حول ميزة sw-precache لاحقًا.)
لتحقيق ذلك:
سيرسل الخادم محتوى HTML الذي يستطيع العميل عرضه ويستخدم عناوين انتهاء صلاحية ذاكرة التخزين المؤقت HTTP المستقبلية لمراعاة المتصفحات التي لا تدعم مشغّلات الخدمات. وسيوفّر هذا البرنامج أسماء ملفات باستخدام علامات تجزئة لإتاحة إمكانية "تحديد الإصدارات" وإجراء تحديثات سهلة لوقت لاحق في دورة حياة التطبيق.
ستتضمن الصفحات أنماط CSS مضمَّنة في علامة
<style>
داخل المستند<head>
من أجل تقديم سرعة العرض الأول لهيكل التطبيق. ستحمّل كل صفحة محتوى JavaScript اللازم لطريقة العرض الحالية بشكلٍ غير متزامن. نظرًا لتعذُّر تحميل صفحات الأنماط المتتالية (CSS) بشكل غير متزامن، يمكننا طلب أنماط باستخدام JavaScript لأنّها غير متزامنة بدلاً من أن تكون مستندة إلى المحلِّل اللغوي ومتزامن. يمكننا أيضًا الاستفادة منrequestAnimationFrame()
لتجنب الحالات التي قد نحصل فيها على نتيجة ذاكرة تخزين مؤقت سريعة وينتهي الأمر بأن تصبح الأنماط عن طريق الخطأ جزءًا من مسار العرض الحرج. تفرضrequestAnimationFrame()
رسم الإطار الأول قبل تحميل الأنماط. ويمكنك أيضًا استخدام مشاريع مثل loadCSS من Filament Group لطلب خدمة CSS بشكل غير متزامن باستخدام JavaScript.يخزِّن مشغّل الخدمات إدخالاً مخزَّنًا مؤقتًا لهيكل التطبيق يتيح تحميل واجهة الأوامر بالكامل من ذاكرة التخزين المؤقت لمشغِّل الخدمات أثناء الزيارات المتكرّرة، ما لم يكن هناك تحديث متاح على الشبكة.
![هيكل التطبيق للمحتوى](https://developer.chrome.google.cn/static/blog/app-shell/image/app-shell-content-a062175a11e75.jpg?authuser=9&hl=ar)
التنفيذ العملي
لقد كتبنا نموذجًا يعمل بشكل كامل باستخدام بنية هيكل التطبيق، وvanilla ES2015 JavaScript للعميل، و Express.js للخادم. بالطبع لا يوجد شيء يمنعك من استخدام المكدس الخاص بك إما لأجزاء العميل أو الخادم (على سبيل المثال PHP، Ruby، Python).
دورة حياة عامل الخدمة
بالنسبة إلى مشروع هيكل التطبيق، نستخدم sw-precache الذي يقدم دورة حياة عامل الخدمة التالية:
حدث | الإجراء |
---|---|
تثبيت | التخزين المؤقت لهيكل التطبيق وموارد التطبيقات الأخرى من صفحة واحدة |
تفعيل | امسح ذاكرات التخزين المؤقت القديمة. |
استدعاء | اعرض تطبيق ويب من صفحة واحدة لعناوين URL واستخدم ذاكرة التخزين المؤقت لمواد العرض والأجزاء المحددة مسبقًا. استخدِم الشبكة للطلبات الأخرى. |
وحدات بت الخادم
في هذه البنية، يجب أن يتمكن مكون جانب الخادم (في حالتنا، المكتوب بلغة Express) من التعامل مع المحتوى والعرض بشكل منفصل. ويمكن إضافة المحتوى إلى تنسيق HTML يؤدي إلى عرض ثابت للصفحة، أو يمكن عرضه بشكل منفصل وتحميله ديناميكيًا.
نتفهّم أنّ الإعداد من جهة الخادم قد يختلف اختلافًا كبيرًا عن الإعداد الذي نستخدمه لتطبيقنا التجريبي. ويمكن تنفيذ هذا النمط من تطبيقات الويب بواسطة معظم عمليات إعداد الخادم، على الرغم من أنّه يتطلب بعض إعادة التصميم. لقد توصلنا إلى أن النموذج التالي يعمل بشكل جيد:
![مخطّط بياني لبنية هيكل التطبيق](https://developer.chrome.google.cn/static/blog/app-shell/image/diagram-the-app-shell-ar-3de72705b132c.png?authuser=9&hl=ar)
وتُحدَّد نقاط النهاية لثلاثة أجزاء من التطبيق: عنوان URL الموجّه للمستخدم (فهرس/حرف بدل)، وهيكل التطبيق (مشغّل الخدمات) وأجزاء HTML.
وتتضمّن كل نقطة نهاية وحدة تحكُّم تتيح استخدام تصميم المقاود، ما يتيح بدوره سحب أجزاء من المقود ومشاهدته. ببساطة، الأجزاء الجزئية هي طرق عرض عبارة عن أجزاء من HTML يتم نسخها إلى الصفحة النهائية. ملاحظة: غالبًا ما تكون أطر عمل JavaScript التي تُجري مزامنة بيانات أكثر تقدّمًا أسهل كثيرًا للانتقال إلى بنية Application Shell. وهم يميلون إلى استخدام ربط البيانات والمزامنة بدلاً من الأجزاء الجزئية.
يتم في البداية عرض صفحة ثابتة تتضمن محتوى للمستخدم. تسجِّل هذه الصفحة مشغّل الخدمات، إذا كان متوافقًا، والذي يخزِّن مؤقتًا هيكل التطبيق وكل ما يعتمد عليه (CSS أو JS أو غير ذلك).
بعد ذلك، سيعمل هيكل التطبيق كتطبيق ويب من صفحة واحدة، مستخدمًا JavaScript إلى XHR في المحتوى لعنوان URL محدد. يتم إجراء طلبات XHR على نقطة نهاية /partials* والتي تعرض الجزء الصغير من HTML وCSS وJS اللازمة لعرض هذا المحتوى. ملاحظة: هناك العديد من الطرق لمعالجة هذا الأمر، ولا شك في أن XHR طريقة واحدة منها فقط. وستُضمِّن بعض التطبيقات بياناتها (ربما باستخدام JSON) للعرض الأولي، وبالتالي لا تكون بيانات "ثابتة" بلغة HTML المسطحة.
يجب أن يتم دائمًا تقديم تجربة احتياطيّة للمتصفّحات التي لا يدعم مشغّل الخدمات. في هذا العرض التوضيحي، نعود إلى العرض الأساسي الثابت من جهة الخادم، ولكن هذا الخيار ليس سوى خيار واحد من بين العديد من الخيارات. يوفر لك جانب مشغّل الخدمات فرصًا جديدة لتحسين أداء تطبيقك بنمط التطبيق المكوّن من صفحة واحدة باستخدام هيكل التطبيق المُخزَّن مؤقتًا.
تحديد إصدارات الملفات
أحد الأسئلة التي تطرأ هو كيفية التعامل مع إصدارات الملفات وتحديثها. يكون ذلك خاصًا بالتطبيق والخيارات هي:
الاتصال بالشبكة أولاً واستخدام النسخة المخزَّنة مؤقتًا بخلاف ذلك.
الاتصال بالشبكة فقط وتعذُّر الاتصال إذا كان الجهاز غير متصل بالإنترنت.
التخزين المؤقت للإصدار القديم والتحديث لاحقًا.
بالنسبة إلى هيكل التطبيق نفسه، يجب اتباع نهج ذاكرة التخزين المؤقت أولاً لإعداد عامل الخدمة. إذا لم تكن تخزن هيكل التطبيق مؤقتًا، فهذا يعني أنك لم تستخدم البنية بشكل صحيح.
الأدوات
ونحتفظ بعدد من مكتبات مساعدي الخدمات المختلفة التي تسهّل عملية إعداد عملية التخزين المؤقت لهيكل التطبيق أو التعامل مع أنماط التخزين المؤقت الشائعة.
![لقطة شاشة لموقع مكتبة "مشغّل الخدمات" الإلكتروني على "أساسيات الويب"](https://developer.chrome.google.cn/static/blog/app-shell/image/screenshot-the-service-w-b2fa896b3fbc4.png?authuser=9&hl=ar)
استخدام sw-precache لهيكل التطبيق
إنّ استخدام sw-precache لتخزين هيكل التطبيق سيعالج المشاكل المتعلقة بنُسخ الملفات السابقة وأسئلة التثبيت/التفعيل وسيناريو الجلب الخاص بهيكل التطبيق. أفلِت sw-precache في عملية إنشاء تطبيقك واستخدِم أحرف البدل القابلة للتهيئة لاختيار الموارد الثابتة. بدلاً من إنشاء نص برمجي لمشغِّل الخدمات يدويًا، يمكنك السماح لميزة sw-precache بإنشاء نص يدير ذاكرة التخزين المؤقت بشكل آمن وفعّال، وذلك باستخدام معالج جلب مخصص لذاكرة التخزين المؤقت أولاً.
تؤدي الزيارات الأولية إلى تطبيقك إلى إجراء حفظ مسبق للمجموعة الكاملة من الموارد المطلوبة. وهي تشبه تجربة تثبيت تطبيق محلي من متجر تطبيقات. وعند عودة المستخدمين إلى تطبيقك، يتم تنزيل الموارد المعدَّلة فقط. وفي هذا العرض التوضيحي، نُعلِم المستخدمين عند توفُّر واجهة جديدة مع الرسالة "تحديثات التطبيق. يُرجى إعادة تحميل الصفحة للوصول إلى الإصدار الجديد". وهذا النمط هو طريقة سهلة تتيح للمستخدمين معرفة أنّ بإمكانهم التحديث للحصول على أحدث إصدار.
استخدام sw-toolbox للتخزين المؤقت في وقت التشغيل
يمكنك استخدام sw-toolbox للتخزين المؤقت في وقت التشغيل باستراتيجيات مختلفة استنادًا إلى المورد:
cacheFirst للصور، إلى جانب ذاكرة تخزين مؤقت مُسمّاة مخصصة لها سياسة انتهاء صلاحية مخصصة لـ N maxEntries.
networkFirst أو الخيار "الأسرع" لطلبات البيانات من واجهة برمجة التطبيقات، وذلك استنادًا إلى مدى حداثة المحتوى المطلوب. قد يكون الخيار الأسرع مفيدًا، ولكن إذا كانت هناك خلاصة واجهة برمجة تطبيقات معيّنة يتم تعديلها بشكل متكرّر، استخدِم NetworkFirst.
الخلاصة
تأتي بُنى هيكل التطبيق مع العديد من المزايا ولكنها تكون منطقية فقط لبعض فئات التطبيقات. لا يزال النموذج صغيرًا وسيستحق تقييم الجهد وفوائد الأداء العامة لهذه البنية.
استفدنا في تجاربنا من مشاركة النماذج بين العميل والخادم لتقليل العمل اللازم لإنشاء طبقتين من التطبيقات. يضمن ذلك أن التحسين التدريجي لا يزال ميزة أساسية.
إذا كنت تفكر بالفعل في استخدام عاملي الخدمات في تطبيقك، ألق نظرة على البنية وقيّم ما إذا كانت منطقية لمشروعاتك الخاصة.
وشكرًا للمراجعين: "جيف بوسنيك" و"بول لويس" و"أليكس راسل" و"سيث تومسون" و"روب دودسون" و"تايلور سافاج" و"جو ميدلي".