تقسيم الشفرة في Angular أو كيفية مشاركة المكونات بين الوحدات الكسولة

ستمنحك هذه المقالة فهمًا أفضل لكيفية تقسيم Angular لشفرتك إلى أجزاء.

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

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

محتويات

  1. لماذا يجب أن أهتم؟
  2. تقسيم كود CLI الزاوي تحت غطاء المحرك
  3. تطبيق زاوي بسيط مع وحدات كسولة
  4. كيفية مشاركة المكونات بين الوحدات الكسولة
  5. خاتمة

لماذا يجب أن أهتم؟

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

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

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

يتطور التطبيق وسرعان ما لاحظت أن وقت بدء تشغيله لا يلبي توقعاتك (والأهم من ذلك ، عميلك).

الآن ، أنت في شك ...

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

دعونا نزيل الغموض!

تقسيم كود CLI الزاوي تحت غطاء المحرك

كما نعلم جميعًا ، يستخدم إصدار Angular CLI الحالي Webpack لأداء التجميع. ولكن على الرغم من ذلك ، فإن حزمة الويب مسؤولة أيضًا عن تقسيم الشفرة.

لذا ، دعنا نلقي نظرة على كيفية قيام webpack بذلك.

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

ولكن قبل تطبيق أمثلية SplitChunksPlugin ، تقوم حزمة الويب بإنشاء جزء جديد:

  • لكل نقطة دخول

يقوم Angular CLI بتكوين نقاط الدخول التالية

أنماط polyfill الرئيسية

مما سيؤدي إلى قطع بالاسم نفسه.

  • لكل وحدة نمطية محملة ديناميكيًا (باستخدام بناء جملة import () الذي يتوافق مع اقتراح ECMAScript لعمليات الاستيراد الديناميكية)

هل تذكر بناء جملة الأطفال؟ هذه هي إشارة حزمة الويب لإنشاء جزء.

الآن دعنا ننتقل إلى SplitChunksPlugin. يمكن تمكينه داخل كتلة التحسين من webpack.config.js

دعونا نلقي نظرة على شفرة مصدر Angular CLI ونجد قسم التكوين هذا:

تكوين SplitChunksPlugin في Angular CLI 8

سنركز على خيارات cacheGroups هنا لأن هذه هي "وصفة" حزمة الويب حول كيفية إنشاء قطع منفصلة على أساس بعض الشروط.

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

كل مجموعة لديها العديد من التكوينات ويمكن أن ترث التكوين من مستوى SplitChunks.

دعنا نذهب بسرعة أكبر من تلك الخيارات التي رأيناها في تكوين Angular CLI أعلاه:

  • يمكن استخدام قيمة القطع لتصفية الوحدات بين قطع المزامنة وغير المتزامنة. يمكن أن تكون قيمته أولية أو غير متزامنة أو كلها. يعني الأولي إضافة الملفات إلى القطعة فقط إذا تم استيرادها داخل قطع المزامنة. يعني المتزامن إضافة ملفات إلى القطعة فقط إذا تم استيرادها داخل قطع متزامن (غير متزامن افتراضيًا)
  • يخبر minChunks Webpack بحقن الوحدات النمطية فقط في القطعة إذا كانت مشتركة بين قطعتين على الأقل (واحدة بشكل افتراضي)
  • يخبر الاسم webpack باستخدام هذا الاسم لمقطع تم إنشاؤه حديثًا. يؤدي تحديد إما سلسلة أو دالة تقوم دائمًا بإرجاع نفس السلسلة إلى دمج جميع الوحدات الشائعة في مقطع واحد.
  • تُستخدم القيمة ذات الأولوية لتحديد الأجزاء الأكثر تطابقًا عندما تندرج الوحدة ضمن العديد من مجموعات المجموعات.
  • يفرض فرض Webpack على تجاهل خيارات minSize و minChunks و maxAsyncRequests و maxInitialRequests وأنشئ دائمًا قطعًا لمجموعة التخزين المؤقت هذه. هناك مسودة صغيرة واحدة هنا: إذا تم توفير أي من هذه الخيارات التي تم تجاهلها على مستوى cacheGroup ، فسيظل هذا الخيار مستخدمًا.
  • اختبار عناصر التحكم التي يتم تحديدها بواسطة مجموعة ذاكرة التخزين المؤقت هذه. كما لاحظنا ، تستخدم Angular CLI هذا الخيار لنقل جميع تبعيات العقدة_العصرية إلى قطعة المورد.
  • يُستخدم minSize لتحديد الحد الأدنى للحجم بالبايت لإنشاء مقطع. لم يظهر في Angular CLI config ولكنه خيار مهم جدًا يجب أن نكون على دراية به. (كما تنص التعليمات البرمجية المصدر ، فإنه 30 كيلو بايت بشكل افتراضي في الإنتاج و 10 كيلو بايت في بيئة التطوير)
نصيحة: على الرغم من حقيقة أن وثائق حزمة الويب تحدد الافتراضات ، أود أن أشير إلى شفرة مصدر حزمة الويب للعثور على القيم الدقيقة

دعونا نلخص هنا: ستنقل Angular CLI وحدة نمطية إلى:

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

يكفي النظرية ، دعونا نمارس.

تطبيق زاوي بسيط مع وحدات كسولة

لشرح عملية SplitChunksPlugin ، سنبدأ بإصدار مبسط من تطبيق Angular:

التطبيق ├── أ (كسول) │ └── a.component.ts │ └── a.module.ts │ ├── ab │ └── ab.component.ts │ └── ab.module.ts │ ├── ب (كسول) │ └── b.component.ts │ └── b.module.ts │ └── ج (كسول) └── └── c.component.ts │ └── ج. ts │ └── cd │ └── cd.component.ts │ └── cd.module.ts │ └── d (كسول) │ └── d.component.ts │ └── d.module.ts │ └── شارك │ └── Shared.module.ts │ └── app.component.ts └── app.module.ts

هنا a و b و c و d هي وحدات كسولة ، بمعنى أنها يتم استيرادها باستخدام بناء الجملة import ().

تستخدم مكونات a و b مكون ab في قوالبها. تستخدم مكونات c و d مكون القرص المضغوط.

التبعيات بين الوحدات الزاوية

الفرق بين ab.module و cd.module هو أنه يتم استيراد ab.module في a.module و b.module بينما يتم استيراد cd.module في Shared.module.

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

خوارزمية

1) تبدأ خوارزمية SplitChunksPlugin بإعطاء كل قطعة تم إنشاؤها سابقًا فهرسًا.

قطع حسب الفهرس

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

chunkSetsInGraph

على سبيل المثال ، 1،2 الرئيسي ، صف polyfill يعني أن هناك وحدة واحدة على الأقل تظهر في جزأين: main و polyfill.

تشترك وحدات a و b في نفس الرمز من ab-module حتى نلاحظ أيضًا التركيبة (4،5) أعلاه.

3) تصفح جميع الوحدات واكتشف ما إذا كان من الممكن إنشاء مجموعة جديدة لمجموعة ذاكرة التخزين المؤقت المحددة.

3 أ) أولاً وقبل كل شيء ، تحدد حزمة الويب ما إذا كان يمكن إضافة وحدة نمطية إلى cacheGroup محددة عن طريق التحقق من خاصية cacheGroup.test.

اختبارات ab.module

اختبار افتراضي غير محدد => موافق
اختبار مشترك غير محدد => موافق
وظيفة اختبار البائع => خطأ

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

اختبارات cd.module هي نفسها.

3 ب) حان الوقت الآن للسير عبر جميع مجموعات القطع.

تتعرف كل وحدة على أي أجزاء تظهر (بفضل خاصية module.chunksIterable).

يتم استيراد ab.module إلى قطعتين كسول. لذا فإن تركيباتها هي (4) و (5) و (4،5).

من ناحية أخرى ، يتم استيراد cd.module فقط في الوحدة النمطية المشتركة ، مما يعني أنه يتم استيرادها فقط في جزء منها. مجموعاتها هي فقط (1).

ثم يقوم المرشح بتصفية المجموعات حسب حجم minChunk:

إذا استمر (chunkCombination.size 

نظرًا لأن ab.module به التركيبة (4،5) ، يجب أن يجتاز هذا الفحص. هذا لا يمكننا أن نقول عن cd.module. عند هذه النقطة ، تبقى هذه الوحدة للعيش داخل القطعة الرئيسية.

3 ج) هناك فحص آخر بواسطة cacheGroup.chunkds (الأولي أو غير المتزامن أو الكل)

يتم استيراد ab.module داخل قطع متزامن (تحميل كسول). هذا هو بالضبط ما تتطلبه مجموعات ذاكرة التخزين المؤقت الافتراضية والمشتركة. بهذه الطريقة تتم إضافة ab.module إلى قطعتين جديدتين ممكنتين (افتراضي وشائع).

لقد وعدت في وقت سابق حتى نذهب هنا.

كيف يولد webpack اسم القطعة التي أنشأتها SplitChunksPlugin؟

يمكن تمثيل النسخة المبسطة لذلك

أين:

  • groupName هو اسم المجموعة (افتراضي في حالتنا)
  • ~ هو مُحدّد تلقائي تلقائي
  • تشير chunkNames إلى قائمة جميع أسماء القطع التي تم تضمينها في تلك المجموعة. هذا الاسم يشبه مسار fullPath ولكن بدلاً من الخط المائل الذي يستخدمه -.

مثال dd-module يعني أن لدينا ملف d.module في مجلد d.

لذلك بعد أن استخدمنا الاستيراد ('./ a / a.module') واستيراد ('./ b / b.module') نحصل

هيكل اسم القطعة الافتراضي

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

بنية اسم القطعة الكبيرة التي تشارك الرمز عبر وحدات كسولة متعددة

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

chunksInfoMap

حان الوقت لتصفية الأجزاء الممكنة

حلقات SplitChunksPlugin على عناصر chunksInfoMap للعثور على أفضل إدخال مطابق. ماذا يعني؟

مجموعة ذاكرة التخزين المؤقت الافتراضية لها الأولوية 10 التي تتجاوز overcomights (التي لديها 5 فقط). هذا يعني أن الافتراضي هو أفضل إدخال مطابق ويجب معالجته أولاً.

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

بهذه الطريقة الافتراضي - aa-module ~ bb-module له الأسبقية على القطعة الشائعة. تتم إزالة هذا الأخير لأنه يحتوي على نفس قائمة الوحدات.

أخيرًا وليس آخرًا ، هي إجراء بعض التحسينات (مثل إزالة التكرارات) والتأكد من استيفاء جميع المتطلبات مثل maxSize.

يمكن العثور على كود المصدر الكامل لـ SplitChunksPlugin هنا

لقد اكتشفنا أن حزمة الويب تنشئ قطعًا بثلاث طرق مختلفة:

  • لكل دخول
  • للوحدات المحملة ديناميكيًا
  • للحصول على كود مشترك بمساعدة SplitChunksPlugin
إخراج CLI الزاوي حسب نوع القطع

الآن دعنا نعود إلى شكوكنا حول ما هي أفضل طريقة للحفاظ على التعليمات البرمجية المشتركة.

كيفية مشاركة المكونات بين الوحدات الكسولة

كما رأينا في تطبيقنا الزاوي البسيط ، أنشأت حزمة الويب جزءًا منفصلاً لـ ab.module لكنها ضمنت cd.module في القطعة الرئيسية.

دعونا نلخص الوجبات الرئيسية من هذا المنشور:

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

خاتمة

آمل الآن أن تفهم بوضوح ناتج Angular CLI وأن تميز بين الدخول والديناميكية والمقسمة باستخدام قطع SplitChunksPlugin.

الترميز سعيدة!