Angular 6+ هو دليل كامل لحقن التبعية. مقدم في مقابل مقدمي: []

الصورة

قدم Angular 6 بنية جديدة محسنة لتضمين تبعيات الخدمة في تطبيق ( provideIn ). على الرغم من حقيقة أن Angular 7 قد تم إصداره بالفعل ، فإن هذا الموضوع لا يزال ذا صلة. هناك الكثير من الارتباك في تعليقات GitHub و Slack و Stack Overflow ، لذلك دعونا نلقي نظرة فاحصة على هذا الموضوع.

سننظر في هذه المقالة في:


  1. حقن التبعية
  2. طريقة قديمة لضخ التبعيات في الزاوي ( مقدمو الخدمة: [] ) ؛
  3. طريقة جديدة لحقن التبعيات في Angular ( providedIn: 'root' | SomeModule ) ؛
  4. UseIn السيناريوهات provideIn ؛
  5. توصيات لاستخدام بناء الجملة الجديد في التطبيقات ؛
  6. لتلخيص.

حقن التبعية


يمكنك تخطي هذا القسم إذا كان لديك بالفعل فكرة عن DI .
حقن التبعية ( DI ) هي طريقة لإنشاء كائنات تعتمد على كائنات أخرى. يوفر نظام حقن التبعية كائنات تابعة عند إنشاء فئة.

- التوثيق الزاوي

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

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

لنفترض في إحدى خدماتنا وجود الرمز التالي:

constructor(private http: HttpClient) 

إذا قمت بإنشائه بدون استخدام آلية حقن التبعية ، يجب عليك إضافة HttpClient يدويًا. ثم سيبدو الرمز على النحو التالي:

 const myService = new MyService(httpClient) 

ولكن أين في هذه الحالة للحصول على httpClient ؟ يجب أيضًا إنشاء:

 const httpClient = new HttpClient(httpHandler) 

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

تقوم آلية حقن التبعية الزاوية بكل هذا تلقائيًا. كل ما نحتاجه هو تحديد التبعيات في مُنشئ المكون وسيتم إضافتها دون أي جهد من جانبنا.

طريقة قديمة لضخ التبعيات في الزاوي (مقدمو الخدمة: [])


لتشغيل التطبيق ، تحتاج Angular إلى معرفة كل كائن فردي نريد تنفيذه في المكونات والخدمات. قبل إصدار Angular 6 ، كانت الطريقة الوحيدة للقيام بذلك هي تحديد الخدمات في خاصية الموفرين: [] decoratorsNgModule و Component و Directive .

الصورة

فيما يلي ثلاثة استخدامات رئيسية لمقدمي: [] :

  1. في الديكور NgModule للوحدة المحملة على الفور ( حريصة ) ؛
  2. في الديكور NgModule لوحدة التحميل المتأخر ( كسول ) ؛
  3. في الديكور Component و Directive .

تحميل الوحدات مع التطبيق (Eager)


في هذه الحالة ، يتم تسجيل الخدمة في النطاق العالمي كـ singleton. وستكون مفردة حتى لو تم تضمينها في مقدمي [] عدة وحدات. يتم إنشاء نسخة واحدة من فئة الخدمة سيتم تسجيلها على مستوى الجذر للتطبيق.

وحدات تحميل مؤجلة (كسول)


سيتم إنشاء مثيل للخدمة المتصلة بالوحدة البطيئة أثناء التهيئة. ستؤدي إضافة هذه الخدمة إلى المكون المتحمس للوحدة النمطية إلى حدوث خطأ: لا يوجد موفر لـ MyService! خطأ .

التنفيذ في @ Component و @ Directive


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

الصورة

في هذه الحالة ، لا يتم تنفيذ RandomService على مستوى الوحدة النمطية وليست مفردة ،
ولكن مسجلة لدى مقدمي: [] من مكون RandomComponent . نتيجة لذلك ، سنحصل على رقم عشوائي جديد في كل مرة عند استخدام <randm> </ randm> .

طريقة جديدة لضخ التبعيات في الزاوي (المقدمة في: 'root' | SomeModule)


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

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

يمكن تضمين الخدمة في جذر التطبيق ( مقدم في: 'root' ) أو في أي وحدة نمطية ( متوفرة في: SomeModule ). providedIn: "root" هو اختصار للتنفيذ في AppModule .

الصورة

لنحلل السيناريوهات الرئيسية لاستخدام بناء الجملة الجديد:

  1. التنفيذ في الوحدة الجذر للتطبيق ( شريطة في: "الجذر" ) ؛
  2. التنفيذ في الوحدة المحملة على الفور ( حريصة ) ؛
  3. التنفيذ في الوحدة مع تأخر التحميل ( كسول ).

التنفيذ في الوحدة النمطية الجذر للتطبيق (المقدمة في: "الجذر")


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

عند استخدام النهج الجديد ، لن يكون هناك اختلاف كبير في تطبيق SPA الأحادي ، حيث يتم استخدام جميع الخدمات المكتوبة ، ومع ذلك يتم توفيرها في: "الجذر" سيكون مفيدًا عند كتابة المكتبات.

في السابق ، كانت جميع خدمات المكتبة بحاجة إلى إضافة إلى مقدمي: [] لوحدتها. بعد استيراد المكتبة إلى التطبيق ، تمت إضافة جميع الخدمات إلى الحزمة ، حتى لو تم استخدام خدمة واحدة فقط. في حالة provideIn: "root" ليست هناك حاجة لتوصيل وحدة المكتبة. ببساطة قم بتضمين الخدمة في المكون المطلوب.

تأخر تحميل وحدة (كسول) ومقدمة في: "الجذر"


ماذا يحدث إذا قمت بتطبيق الخدمة المقدمة مع InIn: "root" في الوحدة النمطية البطيئة ؟

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

كن حذرا! يمكن أن يؤدي تنفيذ خدمة واحدة في وحدات متعددة إلى تبعيات خفية يصعب فهمها ويستحيل تفكيكها.

لحسن الحظ ، هناك طرق لمنع ذلك ، وسننظر فيها أدناه.

حقن التبعية في الوحدة المحملة على الفور (حريصة)


كقاعدة ، هذه الحالة لا معنى لها وبدلاً من ذلك يمكننا استخدامها في المقدمة: 'root' . يمكن استخدام توصيل خدمة في EagerModule للتغليف وسوف يمنع التنفيذ دون توصيل وحدة نمطية ، ولكن هذا ليس ضروريًا في معظم الحالات.

إذا كنت تحتاج حقًا إلى تقييد نطاق الخدمة ، فمن الأسهل استخدام مقدمي الخدمة القدامى : [] الطريقة ، لأنها لن تؤدي بالتأكيد إلى تبعيات دورية.

إذا كان ذلك ممكنًا ، جرب استخدام providedIn: "root" في جميع الوحدات الحريصة.

ملاحظة ميزة وحدات التحميل المتأخر (كسول)


تتمثل إحدى الميزات الرئيسية لـ Angular في القدرة على تقسيم التطبيق بسهولة إلى أجزاء ، مما يمنح المزايا التالية:

  1. الحجم الصغير للحزمة الرئيسية للتطبيق ، والذي يتم من خلاله تحميل التطبيق وبدء تشغيله بشكل أسرع ؛
  2. وحدة التحميل المتأخر معزولة جيدًا ومتصلة في التطبيق مرة واحدة في خاصية loadChildren للطريق المقابل.

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

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

التنفيذ في وحدة نمطية مع تأخير التحميل (المتوفر في: LazyModule)


الصورة

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

حقيقة مثيرة للاهتمام: إذا قمت بتطبيق الخدمة البطيئة في الجزء الرئيسي من التطبيق ، فسوف تفشل عملية التجميع (حتى AOT) بدون أخطاء ، ولكن يتعطل التطبيق بسبب الخطأ "لا يوجد موفر لـ LazyService".

مشكلة التبعية الدورية


الصورة

يمكنك إعادة إنتاج الخطأ كما يلي:

  1. إنشاء وحدة LazyModule ؛
  2. نحن ننشئ خدمة LazyService ونتصل باستخدام المقدمة في: LazyModule ؛
  3. نقوم بإنشاء مكون LazyComponent وربطه بـ LazyModule ؛
  4. إضافة LazyService إلى مُنشئ LazyComponent المكون ؛
  5. نحصل على خطأ مع الاعتماد الدوري.

تخطيطيًا ، يبدو هذا: خدمة -> وحدة نمطية -> مكون -> خدمة .

يمكنك حل هذه المشكلة عن طريق إنشاء وحدة فرعية LazyServiceModule ، والتي سيتم توصيلها بـ LazyModule . توصيل الخدمات إلى الوحدة الفرعية.
الصورة

في هذه الحالة ، سيكون عليك إنشاء وحدة إضافية ، لكنها لن تتطلب الكثير من الجهد وستعطي المزايا التالية:

  1. سيمنع إدخال الخدمة في وحدات التطبيق الأخرى ؛
  2. ستتم إضافة خدمة إلى الحزمة فقط إذا كانت مضمنة في مكون أو خدمة أخرى مستخدمة في الوحدة.

تضمين خدمة في مكون (مقدم في: SomeComponent)


هل من الممكن تضمين خدمة في Component أو Directive باستخدام بناء الجملة الجديد؟

ليس الآن!

لإنشاء مثيل من الخدمة لكل مكون ، ما زلت بحاجة إلى استخدام موفري الخدمة: [] في @ omponent أو Directive decorators .

الصورة

أفضل الممارسات لاستخدام النحو الجديد في التطبيقات


مكتبات


providedIn: "root" مفيد لإنشاء مكتبات. هذه طريقة ملائمة حقًا لتوصيل الجزء المستخدم مباشرةً من الوظائف بالتطبيق الرئيسي وتقليل حجم التجميع النهائي.

أحد الأمثلة العملية هي مكتبة ngx-model ، والتي تمت إعادة كتابتها باستخدام بناء الجملة الجديد وتسمى الآن @ angular-extension / model . في التطبيق الجديد ، ليست هناك حاجة لربط NgxModelModule بالتطبيق ، يكفي فقط تضمين ModelFactory في المكون الضروري. يمكن العثور على تفاصيل التنفيذ هنا .

وحدات التحميل المؤجلة (كسول)


استخدم الوحدة المنفصلة المقدمة: LazyServicesModule للخدمات وقم بتوصيلها في LazyModule . هذا النهج يغلف الخدمات ويمنع توصيلها بوحدات أخرى. سيؤدي ذلك إلى وضع الحدود والمساعدة في إنشاء بنية قابلة للتطوير.

في تجربتي ، يمكن أن يؤدي الإدخال العرضي في الوحدة الرئيسية أو الإضافية (باستخدام providedIn: 'root') إلى الارتباك وليس الحل الأفضل!

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

متى يجب استخدام مقدمي الخدمات: []؟


في الحالات التي يكون فيها من الضروري تكوين الوحدة. على سبيل المثال ، قم بتوصيل الخدمة فقط بـ SomeModule.forRoot (someConfig) .

الصورة

من ناحية أخرى ، في هذه الحالة ، يمكنك استخدام providedIn: "root". سيضمن هذا إضافة الخدمة إلى التطبيق مرة واحدة فقط.

الاستنتاجات


  1. استخدم providedIn: "root" لتسجيل الخدمة كلغة فردية ، متاحة في جميع أنحاء التطبيق.
  2. بالنسبة للوحدة المضمنة في الحزمة الرئيسية ، استخدم providedIn: "root" ، غير متوفرIn: EagerlyImportedModule . في حالات استثنائية ، استخدم موفري: [] للتغليف.
  3. إنشاء وحدة فرعية بخدمات للحد من نطاقها المقدم In: LazyServiceModule عند استخدام التحميل البطيء .
  4. قم بتوصيل وحدة LazyServiceModule في LazyModule لمنع التبعية الدائرية.
  5. استخدم الموفرين: [] في @ omponent و Directive decorators لإنشاء مثيل خدمة جديد لكل مثيل مكون جديد. سيتوفر مثيل الخدمة أيضًا في جميع المكونات الفرعية.
  6. قم دائمًا بتحديد نطاق التبعيات لتحسين الهيكل وتجنب التبعيات المربكة.

المراجع


المقالة الأصلية.
Angular هو مجتمع ناطق بالروسية.
لقاءات الزاوي في روسيا

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


All Articles