AngularJS: `template` vs` templateUrl`

في الأشهر القليلة الماضية ، كنت أبحث في طرق مختلفة لتحسين أداء وقت التشغيل على SPA العملاقة التي أعمل بها في Domo. لقد حققنا بعض التقدم الجاد ، ولكن مع وجود مليون سطر من الكود في أحد المنتجعات ، فإن بعض التغييرات ليست سهلة دائمًا. قام أحد أعضاء فريقنا بهذا الاكتشاف للمساعدة في إضافة التحميل الكسول إلى مشاريع AngularJS ، وقد استثمرنا كثيرًا في هذا. هناك عدد قليل من أعضاء الفريق المختلفين (Jason و Tim) يقومون بمساعدتنا في قياس الوقت الذي يستغرقه التطبيق لدينا للتهيئة بالكامل. لقد استخدمنا أيضًا حزمة الويب لتبسيط الإنشاء ، وكذلك تغيير بعض الأنماط التي نستخدمها. عند دمج حزمة الويب مع ocLazyload ، وجدنا فوزًا كبيرًا لمشاريع AngularJS.

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

كما ترون ، في المثال الأول ، هناك مكون يستخدم templateUrl لتحميل قالبه. هذا هو مشكلة في أحسن الأحوال ، المنظمة البحرية الدولية. هذا يعني أنك ستحتاج إما إلى نشر ملف foo / bar / myComponent.html للإنتاج حتى يتمكن تطبيق الإنتاج من تحميل جزء القالب عبر طلب شبكة ثانٍ للحصول عليه ، أو يعني أنك ستحتاج إلى إضافة بنية الخطوة التي ستعثر على كافة مثيلات templateUrl وإحضار تلك القوالب إلى AngularJS templateCache. كل من هذه الحلول لديها مشاكل.

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

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

بالنظر إلى المئات والمئات من القوالب التي لدينا في مشروعنا ، لم يكن أي من هذه ممكنًا. نحن بحاجة إلى شيء آخر. كنا بحاجة إلى شيء من شأنه أن يسمح لنا بتحميل القوالب الخاصة بنا بكفاءة ، دون طلبات شبكة منفصلة لكل منها ، مع السماح لنا أيضًا بتحميل نفس القوالب بالكامل. لذلك قررنا أن ننظر إلى استخدام أداة تحميل webpack التي تسمح لنا بطلب قوالبنا في مكوناتنا كسلسلة مضمنة من قوالب HTML / Angular.

الفوائد

من خلال استخدام webpack html-loader لتحميل جميع ملفات .html ، اكتشفنا أننا تمكنا من تحميل قوالبنا بكفاءة ، مع السماح لنا أيضًا بالاستفادة الكاملة من التحميل الكسول. عند استخدام القالب: require ('foo / bar / my.html') بناء جملة ، يستبدل webpack عبارة المتطلبات الخاصة بك بوظيفة يتم استدعاءها وإرجاعها بسلسلة القالب. نظرًا لأن القالب يتم توفيره الآن كسلسلة html ، إذا قمت بالكسل في المكون ، فسيتم أيضًا تحميل القالب. هذا هو بالضبط ما نحتاجه. ومع ذلك ، اكتشفنا العديد من الفوائد الأخرى ، التي دفع اكتشافها هذا المنشور.

  • تهيئة مكون أسرع - عند استخدام سلسلة مضمّنة كقالب ، يمكن للمكون التهيئة بشكل متزامن. باستخدام templateUrl ، ستطلب AngularJS القالب من القالب Cache. نظرًا لأن القالب قد يحتوي بالفعل على القالب في ذاكرة التخزين المؤقت الخاصة به ، أو قد يحتاج إلى الانتقال عبر الشبكة للحصول عليه ، فإن طلب قالب من ذاكرة التخزين المؤقت هو عملية تحدث بشكل غير متزامن. حتى إذا كان القالب موجودًا بالفعل في ذاكرة التخزين المؤقت ، فسوف يقوم القالب بإرجاع القالب المخزن مؤقتًا بالفعل عبر مكالمة بناءً على الوعد. هذا يعني أنه لا يمكن تهيئة المكون في حلقة الحدث نفسها. سيتم دائمًا وضع الطلب على القالب في حلقة الحدث التالية ، حتى في أفضل السيناريوهات. هذا يعني أن المكون يمكنه البدء في التهيئة وطلب قالبه ثم الانتهاء من التهيئة في حلقة الحدث التالية. ولكن عند استخدام سلسلة مضمّنة ، يكون المكون بالفعل جاهزًا للقالب ، بحيث يمكنه البدء في إنهائه وتهيئته في حلقة الحدث نفسها. قد لا يبدو هذا مهمًا ، ولكن كان له العديد من النتائج غير المتوقعة التي تعين علينا تعويضها. - تهيئة مكونات أسرع - الأمر الذي يبدو رائعا ، AIR؟ حسنًا ، إنه رائع. ومع ذلك ، فهذا يعني أن بعض مكوناتك التي كان لها دائمًا قيم مدخلاتها محددة عند قد تتم تهيئة التهيئة الخاصة بها ، وقد لا تتواجد تلك القيم نفسها بعد. كان لدينا العديد من المكونات ، بسبب قيم ربط الإدخال غير المحددة. كان علينا تغيير هذه المكونات لاستخدام $ watch أو $ onChanges للكشف عن التحديث لقيم الإدخال. - ستعمل اختبارات الوحدة بطريقة مختلفة - لأن اختبارات الكتابة تتغير عندما تقوم بإجراء اختبار متزامن مقابل اختبار غير متزامن ، فقد يتغير اختبار هذه المكونات بالتأكيد. على سبيل المثال ، في Mocha ، إذا كان الاختبار متزامنًا ، فأنت تقوم بضخ الطريقة المنجزة في الاختبار الخاص بك ، وقمت بالاتصال بها بمجرد الانتهاء من الاختبار. وجدنا أن الاختبارات تعمل الآن بشكل متزامن ، مما يعني أن الحاجة للحقن لم تعد ضرورية. علاوة على ذلك ، من المحرج أن نعترف بهذا ، ولكن كان لدينا اختبارات تمت كتابتها بشكل متزامن ، ومع ذلك ، في الوقت الذي لم يتم فيه القوالب ، فقد تم إكمال تلك الاختبارات بنجاح. لذلك ، عندما ارتكبت التغييرات لتضمين القوالب ، بدأت تلك الاختبارات في التشغيل بنجاح ، وبدلاً من النجاح ، كانت تفشل !!!! في البداية اعتقدت أنني قد كسرت كل تلك الاختبارات. لم أدرك أن هذه الاختبارات لم تمر أبدًا إلا بعد مرور 5 ساعات من الصراخ. لذلك نحن الآن لدينا بالفعل زيادة التغطية التجريبية الآن أننا نستخدم قوالب مضمنة.
  • يستخدم html-loader minifier html - هذه الحقيقة الصغيرة قللت على الفور حجم قوالبنا بنسبة 19 ٪ عبر التطبيق بأكمله. هذا رائع للغاية ، وهو حقًا شيء يجب أن نفعله لفترة طويلة. كما أنه يوزع القوالب ، وساعدنا في العثور على بضع عشرات من القوالب التي تحتوي على html غير صالح فيها. أشياء مثل: فئة "بلاه" ، حيث كانت = مفقودة. أو السمة = {{something}} ، التي تفتقد إلى علامات الاقتباس حولها. بمجرد إصلاح هذه ، عمل البناء مرة أخرى.
  • لا تزال ng-include مقطوعة - بينما كانت قوالب المكونات تعمل الآن ، فإن ng-include قد تم كسرها الآن. كنا بحاجة للتوصل إلى شيء لهم. لذلك قمنا ببناء محمل مخصص صغير ، والذي سوف يجلب القالب إلى القالب. تخبرنا ممارساتنا الداخلية بعدم استخدام ng-include ، ولكن لا يزال لدينا الكثير من الشفرات القديمة التي تزيد عن 3 سنوات والتي تحتوي عليها. لذلك ، بدلاً من refactor كل ذلك في هذا الالتزام ، استخدمت هذا المُحمل الجديد ، وذهبت إلى كل قسم من التطبيق الذي يحتوي على ng-include وتحميل القالب لهذا القسم ، كما هو موضح أدناه. هذا يعني أن نانوغرام تشمل تشمل أيضا في هذه العملية الجديدة.

يستخدم JSCodeShift

أوصي تمامًا باستخدام webpack لتطبيقات AngularJS ، واستخدام html-loader لتضمين القوالب في خطها ، مما يعني أنك ستحتاج إلى تغيير مثيلات القالب إلى مثيلات القالب. نظرًا لأن كل هذه الأشياء تبدو مختلفة تمامًا ، فقد قررت أن هذه كانت حالة استخدامات جيدة جدًا لـ JSCodeShift ، وهو مشروع من Facebook يتيح لك الزحف إلى AST واستبدال جميع المثيلات برمجيًا لك. يمكنك التفكير في الأمر على أنه "العثور على واستبدال المنشطات" ، المحقنة مع المزيد من المنشطات. كان من السهل جدًا كتابة البرنامج النصي الذي عثر على كل هذه الاستخدامات لـ templateUrl وقم بتحديثها: ‘some / url / to.html with template: require (). تمكنت من تغيير 90٪ من الاستخدامات برمجيًا (حوالي 700 ملف) ، واضطررت إلى إنهاء آخر 70 عملية يدويًا. كان بإمكاني كتابة الكود لإنهاء الـ 70 الآخرين ، لكنني اعتقدت أن بإمكاني القيام بذلك أسهل من محاولة ترميز كل منهم على حدة. ملاحظة سريعة ، يعد AST Explorer مطلقة عند استخدام JSCodeShift. وبدون ذلك ، لما تمكنت من إحراز أي تقدم.

خاتمة

احصل على تطبيقات AngularJS الخاصة بك على بنية webpack ، وتضع في الوقت المناسب لاستخدامها في html-loader لتحميل القوالب الخاصة بك. استخدم القالب بدلاً من القالب ، وإذا لم تقم بذلك بالفعل ، فتوقف عن استخدام ng-include. وبعد ذلك ، كسول ، كسول ، كسول! يميز الناس في بعض الأحيان بين التحميل المتأخر والتحميل البطيء. أنا أشير إلى كل من التحميل المتأخر والتحميل الكسول عندما أقول "كسول". إنه أفضل تغيير بالنسبة لك في تقليل الوقت الذي يستغرقه تطبيق First Meanful Paint ، وتقليل وقت امتلاك التطبيق الذي يمكن للمستخدم التفاعل معه. حظا سعيدا. مرة أخرى على رؤوسكم!