التحميل الكسول في Angular: دليل عملي للمبتدئين لفهم NgModules
مقدمة إلى التحميل الكسول في Angular
يُعد Lazy Loading من أهم الأساليب المستخدمة لتحسين أداء تطبيقات Angular، خاصةً عندما يبدأ المشروع في التوسع ويضم عدداً كبيراً من الصفحات والمكوّنات والمكتبات. الفكرة الأساسية بسيطة: بدلاً من تحميل كل شيء دفعة واحدة عند فتح التطبيق، يتم تحميل الأجزاء المطلوبة فقط عند الحاجة إليها.
هذا النهج مهم جداً في تطبيقات SPA أو Single Page Application، لأن التطبيق في هذا النوع يعتمد على واجهة واحدة يتم تحديثها ديناميكياً. وعند تحميل جميع الوحدات منذ البداية، قد يرتفع زمن الإقلاع الأولي بشكل ملحوظ، مما ينعكس سلباً على تجربة المستخدم وعلى مؤشرات الأداء المهمة لمحركات البحث.

ما هو التحميل الكسول ولماذا نحتاج إليه؟
يقصد بـ Lazy Loading تحميل المكوّنات أو الوحدات أو الملفات البرمجية فقط عندما يطلبها المستخدم فعلياً. في المشاريع الصغيرة قد لا يكون الفرق كبيراً، لكن في المشاريع المتوسطة والكبيرة يصبح هذا الأسلوب ضرورياً لتقليل حجم الملفات المحمّلة عند البداية.
عندما يتم تحميل كل شيء دفعة واحدة، فإن التطبيق قد يجلب ملفات ووحدات لا يحتاجها المستخدم في لحظته الحالية. أما عند استخدام التحميل الكسول، فإن Angular يؤجل تحميل بعض الأجزاء إلى حين الانتقال إليها عبر المسارات.
فوائد التحميل الكسول
- تقليل زمن التحميل الأولي للتطبيق.
- تحسين تجربة المستخدم، خاصة على الشبكات البطيئة.
- تقليل استهلاك الموارد في المتصفح.
- تنظيم المشروع إلى وحدات منطقية يسهل صيانتها.
- المساهمة في تحسين الأداء العام، وهو عامل مفيد في
SEO.
ما هي NgModules في Angular؟
لفهم التحميل الكسول بشكل صحيح، يجب أولاً فهم NgModules. في Angular، تُستخدم الوحدات لتنظيم التطبيق إلى أجزاء مترابطة، بحيث تحتوي كل وحدة على مكوّنات وخدمات وتوجيهات وأنابيب مرتبطة بنطاق وظيفي محدد.
من الأمثلة الشائعة على وحدات Angular:
RouterModuleBrowserModuleFormsModule
كما أن بعض الأدوات الخارجية مثل Angular Material تعتمد أيضاً على مفهوم الوحدات.
ماذا يحتوي ملف NgModule عادةً؟
غالباً ما يضم ملف الوحدة العناصر التالية:
declarations: المكوّنات والتوجيهات والأنابيب التابعة للوحدة.imports: الوحدات الأخرى التي تحتاج إليها الوحدة الحالية.providers: الخدمات المتاحة عبر آلية الحقن.bootstrap: المكوّن الجذري الذي يبدأ منه التطبيق.
وأهم ميزة هنا أن هذه الوحدات يمكن تحميلها بطريقة كسولة، أي عند الحاجة فقط.
إنشاء مشروع Angular يدعم التحميل الكسول
سنفترض في هذا المثال أننا سننشئ مشروعاً يحتوي على وحدتين: Module A وModule B، وسيتم تحميل كل واحدة منهما عند الضغط على الزر المرتبط بها.
إنشاء المشروع
يمكنك إنشاء مشروع جديد باسم lazy-load-demo باستخدام الأمر التالي:
ng new lazy-load-demo --routing --style css
هذا الأمر ينشئ مشروع Angular جديداً مع تفعيل التوجيه عبر routing، واختيار CSS كنمط للتنسيق.
الوحدة الجذرية AppModule
عند إنشاء المشروع، يقوم Angular تلقائياً بإنشاء الوحدة الأساسية داخل المسار /src/app. ويكون شكل الملف الأساسي قريباً من المثال التالي:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, AppRoutingModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
في هذا الملف نلاحظ أن المزيّن @NgModule يعرّف الصنف AppModule على أنه وحدة من نوع NgModule.
شرح الحقول الأساسية في NgModule
declarations: يحتوي على المكوّنات التابعة لهذه الوحدة.imports: يضم الوحدات المطلوبة لتعمل الوحدة الحالية بشكل صحيح.providers: يعرّف الخدمات التي يمكن حقنها داخل التطبيق.bootstrap: يحدد المكوّن الرئيسي الذي يتم إدراجه داخل صفحةindex.html.
تصميم الشاشة الرئيسية للتجربة
في الشاشة الرئيسية سنضيف زرين: أحدهما لتحميل Module A والآخر لتحميل Module B. وعند الضغط على أي زر، سيتم توجيه المستخدم إلى المسار المناسب، مما يؤدي إلى تحميل الوحدة المطلوبة فقط.
يمكن استبدال محتوى ملف app.component.html بالكود التالي:
<button style="padding: 20px; color: white; background-color: green;" routerLink="a">Load Module A</button>
<button style="padding: 20px; color: white; background-color: blue;" routerLink="b">Load Module B</button>
<router-outlet></router-outlet>
العنصر router-outlet هو المكان الذي يعرض فيه Angular المكوّن المرتبط بالمسار الحالي.
إنشاء الوحدات المحمّلة كسولاً
لإنشاء وحدات تدعم التحميل الكسول، استخدم الأوامر التالية:
ng generate module modulea --route a --module app.module
ng generate module moduleb --route b --module app.module
بعد تنفيذ هذه الأوامر، سيُنشئ Angular مجلدين باسم modulea وmoduleb. وسيحتوي كل مجلد على ملفات الوحدة والمسارات والمكوّن المرتبط بها.
كيف يضيف Angular المسارات تلقائياً؟
إذا فتحت ملف app-routing.module.ts فستجد إعدادات مشابهة لما يلي:
const routes: Routes = [
{
path: 'a',
loadChildren: () => import('./modulea/modulea.module').then(m => m.ModuleaModule)
},
{
path: 'b',
loadChildren: () => import('./moduleb/moduleb.module').then(m => m.ModulebModule)
}
];
السطر المهم هنا هو loadChildren، لأنه يوضح أن الوحدة لن تُحمّل عند بداية التطبيق، بل عند زيارة المسار a أو b.
تشغيل المشروع ومشاهدة النتيجة
بعد الانتهاء من الإعداد، يمكنك تشغيل المشروع باستخدام الأمر ng serve. عند فتح التطبيق ستظهر الشاشة الرئيسية التي تحتوي على زري التحميل.

عند الضغط على زر Load Module A، سيتم توجيهك إلى صفحة الوحدة الأولى.

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

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

إذا كان كل شيء يعمل بالشكل الصحيح، فستلاحظ طلب ملف خاص بالوحدة الأولى مثل modulea-modulea-module.js، وهذا يؤكد أن الوحدة لم تُحمّل إلا عند الحاجة.

وبالطريقة نفسها، عند الضغط على زر الوحدة الثانية سيظهر ملف moduleb-moduleb-module.js داخل الطلبات.

بعد تحميل الوحدة لأول مرة، لن يعاد تحميل ملف JavaScript نفسه في كل نقرة لاحقة، لأن المتصفح يكون قد خزّنه بالفعل وفقاً لآلية التحميل والتخزين المؤقت.
أهم استخدامات NgModules في المشاريع الواقعية
لا يقتصر استخدام NgModules على الأمثلة التعليمية، بل يظهر أثرها بوضوح في التطبيقات الحقيقية. وفيما يلي بعض السيناريوهات الشائعة:
- فصل شاشات ما قبل تسجيل الدخول عن الشاشات الداخلية بعد تسجيل الدخول.
- تقسيم متجر إلكتروني إلى وحدات خاصة بالعملاء وأخرى خاصة بالبائعين.
- إنشاء وحدة مستقلة لعمليات الدفع لتقليل تحميلها على الزوار الذين لا يحتاجونها مباشرة.
- تجميع العناصر المشتركة في وحدة مثل
CommonModuleتضم مكوّنات وتوجيهات وأنابيب قابلة لإعادة الاستخدام.
أمثلة على العناصر المشتركة داخل وحدة عامة
- أزرار مثل
Copy Code. - مكوّنات التفاعل مثل أزرار التصويت صعوداً وهبوطاً.
- توجيهات مخصّصة قابلة للاستخدام في أكثر من جزء داخل التطبيق.
متى يكون التحميل الكسول ضرورياً؟
إذا كان موقعك أو تطبيقك صغيراً جداً، فقد لا تلاحظ فرقاً هائلاً عند استخدام Lazy Loading. لكن مع زيادة عدد الصفحات، وتعدد مسارات المستخدم، وكثرة المكتبات، يصبح تقسيم التطبيق إلى وحدات قابلة للتحميل عند الطلب قراراً عملياً وذكياً.
الفائدة لا تتعلق بالأداء فقط، بل تشمل أيضاً سهولة التطوير والصيانة وتبسيط بنية المشروع. كما أن تقليل زمن التحميل يسهم في رفع رضا المستخدم، وهو ما ينعكس بصورة إيجابية على مؤشرات الاستخدام والظهور في نتائج البحث.
نصائح عملية لتحسين الأداء مع Lazy Loading
- قسّم التطبيق إلى وحدات منطقية واضحة بدلاً من تقسيم عشوائي.
- لا تضع كل المكوّنات المشتركة داخل كل وحدة؛ استخدم وحدة مشتركة مخصصة لذلك.
- راقب حجم الحزم الناتجة بعد البناء باستخدام أدوات تحليل الحِزم.
- احرص على أن تكون المسارات التي يزورها المستخدمون نادراً مرشحة ممتازة للتحويل إلى وحدات محمّلة كسولاً.
- اختبر الأداء من خلال تبويب
Networkوأدوات مثلLighthouse.
الخلاصة التقنية
يُعد Lazy Loading في Angular من أفضل الأساليب لتطوير تطبيقات سريعة وقابلة للتوسع. وعند دمجه مع بنية صحيحة لوحدات NgModules، يمكن تقليل التحميل الأولي بشكل ملحوظ وتحسين تجربة الاستخدام وتنظيم الكود على المدى الطويل. من الناحية التقنية، كلما زاد حجم التطبيق، أصبحت هذه الممارسة أقرب إلى ضرورة وليست مجرد تحسين اختياري.