قم بتحسين أداء SPA عن طريق تقسيم مكتبات Angular إلى عدة أجزاء

مرحبا يا هبر! أقدم إليكم ترجمة المقال "تحسين أداء واس من خلال تقسيم المكتبات الزاوي الخاص بك في أجزاء متعددة" بقلم كيفن كروزر .


الزاوي هو إطار كبير. نحن جميعا نحبه <3.


واحدة من الأشياء التي تجعل Angular ناجحة وجميلة في نفس الوقت هي المجتمع الواسع والقيمة التي يحملها. هناك العديد من الاجتماعات والمدونات والمؤتمرات ، وبالطبع ، المكتبات الزاوية.


بفضل Angular CLI ، أصبح من السهل إنشاء المكتبات اليوم. إنها رائعة لمشاركة الكود بين تطبيقات متعددة.


نظرًا لأنه يمكن استخدامها في العديد من الأماكن ، يعد الأداء جانبًا مهمًا. يمكن أن تبطئ المكتبة ذات الأداء الضعيف تطبيقات متعددة!


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


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


"هذه مكتبة بسيطة. لا يمكن أن تؤثر على الأداء ، أليس كذلك؟"


لنبدأ بمكتبة بسيطة سنقوم بإنشائها باستخدام Angular CLI. إذا لم تنشئ مكتبة Angular أبدًا ، فقد يكون من المفيد لك قراءة المقالة التالية:


صورة


بمجرد استخدام CLI لتكوين الكثير من مساحة عمل المشروع ، يمكننا البدء في إضافة رمز.


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


صورة


مجرد وحدة نمطية Angular و Component تأخذ خاصية اسم أعلى روابط الإدخال وتعرضها.


صورة


HowdyTimeComponent هي المسؤولة عن عرض الوقت باستخدام مكتبة لحظات تابعة لجهة خارجية.


! ممتاز لدينا مكتبة مرحبا استعداد للنشر! إنها مكتبة بسيطة ؛ إنها لن تكون قادرة على التأثير على الأداء ، أليس كذلك؟


مرحبا مكتبة الاستهلاك


الآن لدينا مكتبة مرحبا! سيكون من العار عدم الاستفادة من هذا. لاستخدام مكتبة howdy ، نقوم بإنشاء SPA جديد مع Angular CLI.


ng new greeting-app 

بما أننا مهتمون بالأداء ، فلنقم أيضًا بتثبيت تبعية dev التي تسمى webpack-bundle-analys .


 npm i -D webpack-bundle-analyzer 

يسمح لك Webpack-bundle-analys بتصور حجم ملفات إخراج webpack باستخدام خريطة شجرة تفاعلية قابلة للتطوير.


أفضل طريقة لتحليل الحزمة الخاصة بنا هي إضافة البرنامج النصي للتحليل التالي إلى package.json .


 "analyze": "ng build --prod --stats-json && webpack-bundle-analyzer ./dist/greeting-app/stats-es2015.json" 

إذا قمنا بتشغيل هذا الأمر ، فستقوم Angular بإنشاء الإنتاج وأيضًا إخراج stats-es2015.json ، والذي سيتم تحديده وتقديمه بواسطة webpack-bundle-anlyzer .


صورة


نظرًا لأننا لم نكتب أي كود بعد ، فإن الحزمة الرئيسية لدينا تتكون أساسًا من Angular. يمكننا أن نرى أيضا أن zone.js مدرج في حزمة polyfill لدينا.


بشكل عام ، حجم التطبيق لدينا الآن 207 كيلو بايت .


لكننا لم نقم بتضمين مكتبة Howdy الخاصة بنا حتى الآن! دعنا نمضي قدما ونفعل ذلك.


 npm i howdy 

لقد قمنا بتثبيت مكتبة howdy لأننا نريد استضافة تحية باسم. نحن لسنا مهتمين بمظاهرة الزمن. لذلك ، سوف نستخدم فقط الوحدة النمطية HowdyNameModule ولن نقوم بتضمين HowdyTimeModule .


صورة


من المهم أن نلاحظ هنا أننا نستورد HowdyNameModule فقط . لنقم بتشغيل تحليل النص مرة أخرى.


صورة


نجاح باهر! رائع جدا! تحولنا من 207 كيلو بايت إلى 511.15 كيلو بايت. حجم أكثر من الضعف. يا له من ...!


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


بالطبع ، يمكن استبدال اللحظة بحزم أخرى ، مثل fn-date أو min -mini . لكن السؤال مختلف. لماذا هو حتى هناك؟ تذكر أننا استوردنا فقط HowdyNameModule ، وليس HodwyTimeModule . اعتقدت أنه عند حدوث اهتزاز الشجرة ، تتخلص الوحدات غير المستخدمة فقط؟ ما الذي يحدث؟


شجرة الهز قد لا يزيل كل شيء


من أجل أن يحدث اهتزاز الشجرة ، تطلق البنية Angular مجموعة من التحسينات المتقدمة. ولكن لا يزال ، هذه اللحظة موجودة في المجموعة ، على الرغم من أن HowdyTimeModule ليس كذلك.
المشكلة هي كيف يتم حزم اللحظة. دعونا نلقي نظرة سريعة على ملف moment.js في مجلد node_modules الخاص بنا.


صورة


نظرًا لأنه يمكن استخدام اللحظة في العديد من الأماكن ، مثل Node JS backend أو التطبيقات Angular أو JavaScript العادية ، فهي مجمعة في UMD وليس كوحدة ES .


يتم التفاف مكتبات UMD المرتبطة في وظيفة IFFE ، مما يعني أنه لا يمكن استخدام ModuleConcatenation . لا يمكن لأدوات تحسين التجميع معرفة ما إذا كان سيتم استخدام هذا الرمز أو إذا كان له آثار جانبية.


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


لسوء الحظ ، لا يمكننا التحكم في كيفية اكتمال اللحظة . هل هذا يعني أننا يجب أن نتحمل الحجم الهائل للحزمة؟


نقاط دخول ثانوية للنصر


لا يمكننا التحكم في كيفية خلق اللحظة . لكن يمكننا إدارة مكتبتنا. في الواقع ، هناك طريقة لمنع مثل هذه السيناريوهات. نقاط الدخول الثانوية!


يتم حاليًا تجميع جميع المكتبات الزاوية تقريباً باستخدام ng-packagr . يسمح لك ng-packagr باستخدام ng-package.json بالاشتراك مع public-api ، والتي ستصبح في نهاية المطاف نقطة الدخول إلى التطبيق الخاص بك.


كما يوحي الاسم ، تتيح لك نقاط الإدخال الإضافية تحديد نقاط إدخال متعددة للتطبيق الخاص بك.


هذا يبدو جيدا! كيفية تفعيل نقاط الدخول الثانوية؟


يتم اكتشاف نقاط الدخول الثانوية بشكل حيوي باستخدام ng-packagr . يبحث ng-packagr عن ملفات package.json في الدلائل الفرعية للمجلد الرئيسي لملف package.json


باردة! دعنا نستفيد من نقاط الإدخال الثانوية في مكتبتنا الهادئة عن طريق إضافة الملفات التالية.


صورة


لكل وحدة ، أضفنا index.ts ، package.json و public_api.ts .


  • index.ts موجود ، فقط للإشارة إلى public_api ، وهو أمر مفيد أثناء الاستيراد.
  • public_api يصدر جميع الوحدات والمكونات من الوحدة النمطية لدينا.
  • package.json يحتوي على تكوينات ng-packagr محددة. في حالتنا ، هذا يكفي لتحديد enterFile .

قد تحتوي Package.json أيضًا على خصائص أخرى ، مثل cssUrl ، إلخ. لاحظ أن نطاق هذه الخصائص هو العنصر الفرعي الحالي فقط.

إذا قمنا بإدارة المجموعة الآن ، فسنحصل على ثلاث كتل. howdy.js ، howdy-src-lib-name.js و howdy-src-lib-time.js .


يحتوي Howdy-src-lib-name.js الآن فقط على رمز مرتبط بـ HowdyNameModule ، ويتضمن howdy-src-lib-time.js الآن فقط رمزًا خاصًا بـ HowdyTimeModule .


ولكن دعونا ننظر إلى قطعة من howdy.js .


صورة


لا تزال تحتوي قطعة howdy.js على HowdyNameComponent و HowdyTimeComponent . هذا يعني أننا ما زلنا نحصل على هذه اللحظة ، حتى لو قمنا باستيراد HowdyNameModule فقط.


إذا كنا نريد التخلص من HowdyTimeModule مع هذا النهج ، نحتاج إلى استخدام الاستيراد العميق. لذلك نحن نستورد ليس من howdy.js ، ولكن مباشرة من howdy-src-lib-time.js
ما لا ينصح! الواردات العميقة تشكل خطورة ويجب تجنبها دائمًا!

كيف يمكننا حل هذه المشاكل؟ كيف يمكننا أن نضمن أن HowdyTimeModule ستتم إزالته أيضًا حتى لو استخدمنا الواردات القياسية؟ حسنًا ، نحتاج إلى إعداد طريقة لإنشاء قطعة من howdy.js .


استخدام "علامة"


تتمثل الفكرة في إزالة الشفرة من كتلة howdy.js والسماح لها بدلاً من ذلك بالعمل كنوع من "مؤشر" الإشارة "الذي يشير بك إلى كتل أخرى.


لذلك دعونا نلقي نظرة فاحصة على src / public_api.ts .


 /* * Public API Surface of howdy */ export * from './lib/name/howdy-name.component'; export * from './lib/name/howdy-name.module'; export * from './lib/time/howdy-time.component'; export * from './lib/time/howdy-time.module'; 

هذه الخطوط مسؤولة عن تضمين كل شيء بدءًا من الاسم والوقت في كتلة howdy.js . نحتاج إلى إزالة الشفرة من howdydy.js والسماح لها بالإشارة إلى الأجزاء الأخرى التي تحتوي على التطبيق. دعنا نغير محتواه.


 / * Public API Surface of howdy */ export * from 'howdy/src/lib/name'; export * from 'howdy/src/lib/time'; 

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


صورة


يعمل Howdy.js الآن كـ "مؤشر" يشير إلى الأجزاء التي تحتوي على التطبيق. تحتوي كتلة howdy-src-lib-name.js فقط على الرمز من مجلد الاسم ، بينما يحتوي ملف howdy-src-lib-time.js على الرمز من مجلد الوقت فقط .


أكمل الحزمة بالبدائل


دعونا تحديث الحزمة مرحبا في تطبيق الترحيب لدينا وإعادة تشغيل البرنامج النصي التحليل.


صورة


بارد. حجم الحزمة الآن 170.94 كيلو بايت . أعلى بقليل من الأصل. دعونا نرى كيف تبدو وحدة Howdy في الحزمة النهائية.


صورة


! رائع يسمح لنا هذا التعديل بالحفاظ على حجم العبوة الذي يستهلك SPA صغيرًا. واس يحصل فقط على ما يحتاجون إليه!


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

تجربة حقيقية


المثال أعلاه بسيط للغاية.


ومع ذلك ، بعد إدخال نقاط الدخول الثانوية في مشروع الشركة الحالي ، سيكون كل شيء مختلفًا. عليك التعامل مع تعقيد أكبر بكثير ، في حين أن رسائل الخطأ من ng-packagr ليست مفيدة دائمًا.


على الأرجح ، ستحتاج إلى تكوين بعض مسارات الاستيراد أو تحديد بعض المسارات في ملف tsconfig.json . وستصادف أيضًا وحدات من كتلة واحدة تستخدم وحدات من كتلة أخرى.


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


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


في مرحلة ما ، ارتفع حتى 5 ميغابايت . تلقى كل SPA لحظة ، simlane / datatable وأشياء أخرى لم يستخدمها حتى. بدأنا التركيز على تحسين حجم الحزمة.


لقد أزلنا اللحظة من fn-date وبدأنا في استخدام نقاط الدخول الثانوية. حاليًا ، تلقينا وحدة رئيسية بحجم 662 كيلو بايت لـ SPA الذي تم إنشاؤه حديثًا ، والذي يتضمن العديد من المكتبات. هذا لا يزال كثيرًا ، لكننا لم ننته بعد. لم يكتمل التحسين بعد - يمكننا تقليل حجم الحزمة بدرجة أكبر.


من الرائع أن نرى من أين نحن ومن أين أتينا.


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


استنتاج


يقوم Angular بعمل رائع عندما يتعلق الأمر بتحسين حجم العبوة. على الرغم من أن خطوات التجميع للتحسين معقدة للغاية ، إلا أنها لا تستطيع التخلص من اهتزاز الشجرة.


الوحدات النمطية المعبأة في تنسيقات غير ESModules لا يمكن تهتز بها الأشجار.


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


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

Source: https://habr.com/ru/post/ar482646/


All Articles