22 نصيحة لمطور Angular. الجزء الأول

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


ستركز هذه السلسلة من المواد على مناهج التطوير التي يستخدمها فريق Trade Me ، والتي يتم التعبير عنها في شكل أكثر من عشرين توصية تتعلق بتقنيات مثل Angular و TypeScript و RxJS و @ ngrx / store. بالإضافة إلى ذلك ، سيتم إيلاء بعض الاهتمام لتقنيات البرمجة العالمية التي تهدف إلى جعل رمز التطبيق أنظف وأكثر دقة.

1. حول trackBy


باستخدام ngFor لاجتياز المصفوفات في القوالب ، استخدم هذا البناء مع وظيفة trackBy ، التي تُرجع معرّفًا فريدًا لكل عنصر.

▍ تفسيرات


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

إلى


 <li *ngFor="let item of items;">{{ item }}</li> 

fter بعد ذلك


 //   <li *ngFor="let item of items; trackBy: trackByFn">{{ item }}</li> //   trackByFn(index, item) {     return item.id; //  id,   } 

2. الكلمات الدالة الثابتة والسماح


إذا كنت ستعلن عن متغير لا تنوي تغيير قيمته ، فاستخدم الكلمة الأساسية const .

▍ تفسيرات


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

إلى


 let car = 'ludicrous car'; let myCar = `My ${car}`; let yourCar = `Your ${car}; if (iHaveMoreThanOneCar) {  myCar = `${myCar}s`; } if (youHaveMoreThanOneCar) {  yourCar = `${youCar}s`; } 

fter بعد ذلك


 //  car  ,     car  const car = 'ludicrous car'; let myCar = `My ${car}`; let yourCar = `Your ${car}; if (iHaveMoreThanOneCar) {  myCar = `${myCar}s`; } if (youHaveMoreThanOneCar) {  yourCar = `${youCar}s`; } 

3. مشغلي الناقل


عند العمل مع RxJS ، استخدم مشغلي خطوط الأنابيب.

▍ تفسيرات


يدعم المشغلون المنقولون خوارزمية اهتزاز الأشجار ، أي عندما يتم استيرادهم ، سيتم تضمين الرمز الذي سيتم تنفيذه فقط في المشروع. يسهّل هذا أيضًا تحديد العبارات غير المستخدمة في الملفات.

يرجى ملاحظة أن هذه التوصية ذات صلة بالإصدار 5.5 من Angular والإصدارات الأحدث.

إلى


 import 'rxjs/add/operator/map'; import 'rxjs/add/operator/take'; iAmAnObservable   .map(value => value.item)   .take(1); 

fter بعد ذلك


 import { map, take } from 'rxjs/operators'; iAmAnObservable   .pipe(      map(value => value.item),      take(1)    ); 

4. عزل إصلاحات API


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

▍ تفسيرات


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

بالإضافة إلى ذلك ، يمكنك إنشاء API_FIX الخاصة ، مثل API_FIX (التي تشبه علامة TODO ) ، ووضع علامة على التصحيحات معهم. هذا يجعل من السهل العثور على مثل هذه الإصلاحات.

5. الاشتراك في القالب


تجنب الاشتراك في العناصر القابلة للرصد من المكونات. بدلاً من ذلك ، اشترك فيها في قوالب.

▍ تفسيرات


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

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

إلى


 //  <p>{{ textToDisplay }}</p> //  iAmAnObservable   .pipe(      map(value => value.item),      takeUntil(this._destroyed$)    )   .subscribe(item => this.textToDisplay = item); 

fter بعد ذلك


 //  <p>{{ textToDisplay$ | async }}</p> //  this.textToDisplay$ = iAmAnObservable   .pipe(      map(value => value.item)    ); 

6. إزالة الاشتراكات


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

▍ تفسيرات


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

والأفضل من ذلك إنشاء قاعدة اللتر للكشف عن الكائنات التي تمت ملاحظتها من خلال اشتراك صالح لها.

إلى


 iAmAnObservable   .pipe(      map(value => value.item)        )   .subscribe(item => this.textToDisplay = item); 

fter بعد ذلك


استخدم عامل takeUntil إذا كنت ترغب في ملاحظة التغييرات التي تطرأ على بعض الكائنات حتى takeUntil آخر قيمة معينة:

 private destroyed$ = new Subject(); public ngOnInit (): void {   iAmAnObservable   .pipe(      map(value => value.item)     //    iAmAnObservable         takeUntil(this._destroyed$)    )   .subscribe(item => this.textToDisplay = item); } public ngOnDestroy (): void {   this._destroyed$.next(); } 

استخدام شيء مثل this هو نمط يستخدم للتحكم في إزالة الاشتراكات للعديد من الكائنات المرصودة في المكون.

استخدم take إذا كنت بحاجة فقط إلى القيمة الأولى التي أرجعها الكائن المرصود:

 iAmAnObservable   .pipe(      map(value => value.item),      take(1),      takeUntil(this._destroyed$)   )   .subscribe(item => this.textToDisplay = item); 

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

7. استخدام عوامل التشغيل المناسبة


باستخدام عوامل التمليس مع كائنات يمكن ملاحظتها ، قم بتطبيق تلك التي تتوافق مع ميزات المشكلة التي يتم حلها.

  • استخدم switchMap عندما تحتاج إلى تجاهل الإجراء المجدول السابق عند وصول إجراء جديد.
  • استخدم mergeMap في حال احتجت لمعالجة جميع الإجراءات المرسلة بالتوازي.
  • استخدم concatMap عندما يلزم معالجة الإجراءات واحدة تلو الأخرى ، بالترتيب الذي تم تلقيها به.
  • استخدم exhaustMap في المواقف التي exhaustMap فيها ، أثناء معالجة الإجراءات المستلمة مسبقًا ، إلى تجاهل إجراءات جديدة.

يمكن العثور على تفاصيل حول هذا هنا .

▍ تفسيرات


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

8. تحميل كسول


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

▍ تفسيرات


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

إلى


 // app.routing.ts { path: 'not-lazy-loaded', component: NotLazyLoadedComponent } 

fter بعد ذلك


 // app.routing.ts { path: 'lazy-load', loadChildren: 'lazy-load.module#LazyLoadModule' } // lazy-load.module.ts import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; import { LazyLoadComponent }   from './lazy-load.component'; @NgModule({ imports: [   CommonModule,   RouterModule.forChild([        {            path: '',            component: LazyLoadComponent        }   ]) ], declarations: [   LazyLoadComponent ] }) export class LazyModule {} 

9. حول الاشتراكات ضمن اشتراكات أخرى


في بعض الأحيان ، للقيام ببعض الإجراءات ، قد تحتاج إلى بيانات من عدة كائنات يمكن ملاحظتها. في مثل هذه الحالة ، تجنب إنشاء اشتراكات لهذه الكائنات داخل كتل subscribe لكائنات أخرى يمكن ملاحظتها. بدلاً من ذلك ، استخدم عوامل تشغيل مناسبة لسلسلة الأوامر. من بين هذه العوامل ، يمكن للمرء أن يلاحظ مع combineLatest . فكر في الأمثلة ، ثم قم بالتعليق عليها.

إلى


 firstObservable$.pipe(  take(1) ) .subscribe(firstValue => {   secondObservable$.pipe(       take(1)   )   .subscribe(secondValue => {       console.log(`Combined values are: ${firstValue} & ${secondValue}`);   }); }); 

fter بعد ذلك


 firstObservable$.pipe(   withLatestFrom(secondObservable$),   first() ) .subscribe(([firstValue, secondValue]) => {   console.log(`Combined values are: ${firstValue} & ${secondValue}`); }); 

▍ تفسيرات


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

10. حول الكتابة


حاول دائمًا تعريف المتغيرات أو الثوابت بنوع غير any نوع.

▍ تفسيرات


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

 const x = 1; const y = 'a'; const z = x + y; console.log(`Value of z is: ${z}` //  Value of z is 1a 

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

نعيد كتابة المثال أعلاه:

 const x: number = 1; const y: number = 'a'; const z: number = x + y; //    : Type '"a"' is not assignable to type 'number'. const y:number 

هذا يساعد على تجنب أخطاء نوع البيانات.

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

فكر في مثال:

 public ngOnInit (): void {   let myFlashObject = {       name: 'My cool name',       age: 'My cool age',       loc: 'My cool location'   }   this.processObject(myFlashObject); } public processObject(myObject: any): void {   console.log(`Name: ${myObject.name}`);   console.log(`Age: ${myObject.age}`);   console.log(`Location: ${myObject.loc}`); } //  Name: My cool name Age: My cool age Location: My cool location 

لنفترض أننا أردنا تغيير اسم خاصية loc في location في myFlashObject خطأ myFlashObject تحرير الكود:

 public ngOnInit (): void {   let myFlashObject = {       name: 'My cool name',       age: 'My cool age',       location: 'My cool location'   }   this.processObject(myFlashObject); } public processObject(myObject: any): void {   console.log(`Name: ${myObject.name}`);   console.log(`Age: ${myObject.age}`);   console.log(`Location: ${myObject.loc}`); } //  Name: My cool name Age: My cool age Location: undefined 

إذا لم يتم استخدام الكتابة عند إنشاء كائن myFlashObject ، في حالتنا ، يفترض النظام أن قيمة الخاصية loc لـ myFlashObject غير undefined . لا تعتقد أن loc قد يكون اسم خاصية غير صالح.

إذا تم استخدام الكتابة عند وصف كائن myFlashObject ، فسوف نرى في حالة مماثلة ، عند تجميع الكود ، رسالة خطأ رائعة:

 type FlashObject = {   name: string,   age: string,   location: string } public ngOnInit (): void {   let myFlashObject: FlashObject = {       name: 'My cool name',       age: 'My cool age',       //         Type '{ name: string; age: string; loc: string; }' is not assignable to type 'FlashObjectType'.       Object literal may only specify known properties, and 'loc' does not exist in type 'FlashObjectType'.       loc: 'My cool location'   }   this.processObject(myFlashObject); } public processObject(myObject: FlashObject): void {   console.log(`Name: ${myObject.name}`);   console.log(`Age: ${myObject.age}`)   //     Property 'loc' does not exist on type 'FlashObjectType'.   console.log(`Location: ${myObject.loc}`); } 

إذا كنت تبدأ العمل في مشروع جديد ، فسيكون من المفيد أن تقوم ، في ملف tsconfig.json ، tsconfig.json الخيار strict:true أجل تمكين التحقق الصارم من النوع.

11. حول استخدام اللنت


يحتوي Tslint على قواعد قياسية مختلفة مثل no-no ، no-magic-number ، no-console . يمكن تخصيص اللنت عن طريق تحرير ملف tslint.json لتنظيم التحقق من التعليمات البرمجية وفقًا لقواعد معينة.

▍ تفسيرات


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

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

إلى


 public ngOnInit (): void {   console.log('I am a naughty console log message');   console.warn('I am a naughty console warning message');   console.error('I am a naughty console error message'); } // .    ,    : I am a naughty console message I am a naughty console warning message I am a naughty console error message 

fter بعد ذلك


 // tslint.json {   "rules": {       .......       "no-console": [            true,            "log", //  console.log             "warn" //  console.warn        ]  } } // ..component.ts public ngOnInit (): void {   console.log('I am a naughty console log message');   console.warn('I am a naughty console warning message');   console.error('I am a naughty console error message'); } // .      console.log and console.warn        console.error,         Calls to 'console.log' are not allowed. Calls to 'console.warn' are not allowed. 

الملخص


استعرضنا اليوم 11 توصية نأمل أن تكون مفيدة لمطوري Angular. في المرة القادمة ، انتظر 11 نصيحة أخرى.

أعزائي القراء! هل تستخدم الإطار الزاوي لتطوير مشاريع الويب؟

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


All Articles