Der Autor des Artikels, dessen erster Teil der Übersetzung wir veröffentlichen, sagt, dass er seit etwa zwei Jahren an einer groß angelegten Angular-Anwendung in 
Trade Me arbeitet . In den letzten Jahren hat das Anwendungsentwicklungsteam das Projekt sowohl hinsichtlich der Codequalität als auch der Leistung ständig verbessert.
Diese Reihe von Materialien konzentriert sich auf die Entwicklungsansätze des Trade Me-Teams, die in Form von mehr als zwei Dutzend Empfehlungen zu Technologien wie Angular, TypeScript, RxJS und @ ngrx / store zum Ausdruck kommen. Darüber hinaus wird universellen Programmiertechniken besondere Aufmerksamkeit gewidmet, die darauf abzielen, den Anwendungscode sauberer und genauer zu machen.
1. Über trackBy
Verwenden 
ngFor dieses Konstrukt mit 
ngFor zum Durchlaufen von Arrays in Vorlagen mit der Funktion 
trackBy , die für jedes Element einen eindeutigen Bezeichner zurückgibt.
▍ Erklärungen
Wenn sich das Array ändert, rendert Angular den gesamten DOM-Baum neu. Wenn Sie jedoch 
trackBy , weiß das System, welches Element geändert wurde, und nimmt Änderungen am DOM vor, die nur für dieses bestimmte Element gelten. Details dazu finden Sie 
hier .
»Um
 <li *ngFor="let item of items;">{{ item }}</li> 
▍Nachher
 //   <li *ngFor="let item of items; trackBy: trackByFn">{{ item }}</li> //   trackByFn(index, item) {     return item.id; //  id,   } 
2. Schlüsselwörter const und let
Wenn Sie eine Variable deklarieren möchten, deren Wert Sie nicht ändern 
const , verwenden Sie das Schlüsselwort 
const .
▍ Erklärungen
Die angemessene Verwendung der Schlüsselwörter 
let und 
const verdeutlicht die Absicht hinsichtlich der Verwendung von Entitäten, deren Verwendung deklariert wurde. Darüber hinaus erleichtert dieser Ansatz das Erkennen von Problemen, die durch versehentliches Überschreiben konstanter Werte verursacht werden. In dieser Situation wird ein Kompilierungsfehler ausgelöst. Darüber hinaus wird die Lesbarkeit des Codes verbessert.
»Um
 let car = 'ludicrous car'; let myCar = `My ${car}`; let yourCar = `Your ${car}; if (iHaveMoreThanOneCar) {  myCar = `${myCar}s`; } if (youHaveMoreThanOneCar) {  yourCar = `${youCar}s`; } 
▍Nachher
 //  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. Fördererbetreiber
Verwenden Sie bei der Arbeit mit RxJS Pipeline-Operatoren.
▍ Erklärungen
Die übermittelten Operatoren unterstützen den Tree-Shake-Algorithmus, dh, wenn sie importiert werden, wird nur der Code in das Projekt aufgenommen, dessen Ausführung geplant ist. Dies macht es auch einfach, nicht verwendete Anweisungen in Dateien zu identifizieren.
Bitte beachten Sie, dass diese Empfehlung für Angular Version 5.5 und höher relevant ist.
»Um
 import 'rxjs/add/operator/map'; import 'rxjs/add/operator/take'; iAmAnObservable   .map(value => value.item)   .take(1); 
▍Nachher
 import { map, take } from 'rxjs/operators'; iAmAnObservable   .pipe(      map(value => value.item),      take(1)    ); 
4. Isolierung von API-Fixes
Nicht alle APIs sind vollständig stabil und fehlerfrei. Daher ist es manchmal erforderlich, eine Logik in den Code einzuführen, um API-Probleme zu beheben. Anstatt diese Logik in Komponenten zu platzieren, in denen gepatchte APIs verwendet werden, ist es besser, sie irgendwo zu isolieren, z. B. in einem Dienst, und auf den entsprechenden Dienst zu verweisen, anstatt auf die problematische API der Komponente.
▍ Erklärungen
Der vorgeschlagene Ansatz ermöglicht es, Korrekturen „näher“ an der API zu halten, dh so nah wie möglich an dem Code, aus dem Netzwerkanforderungen gestellt werden. Infolgedessen wird die Menge an Anwendungscode, der mit problematischen APIs interagiert, reduziert. Darüber hinaus stellt sich heraus, dass sich alle Korrekturen an einem Ort befinden, wodurch es einfacher wird, mit ihnen zu arbeiten. Wenn Sie Fehler in der API beheben müssen, ist es viel einfacher, dies in einer einzigen Datei zu tun, als diese Korrekturen in der gesamten Anwendung zu verteilen. Dies erleichtert nicht nur die Erstellung von Korrekturen, sondern auch die Suche nach dem geeigneten Code im Projekt und dessen Unterstützung.
Darüber hinaus können Sie eigene Tags erstellen, z. B. 
API_FIX (ähnlich dem 
TODO Tag), und Korrekturen damit 
API_FIX . Dies erleichtert das Auffinden solcher Korrekturen.
5. Abonnement in der Vorlage
Vermeiden Sie das Abonnieren von Observablen aus Komponenten. Abonnieren Sie sie stattdessen in Vorlagen.
▍ Erklärungen
Asynchrone Pipeline-Betreiber melden sich automatisch ab, was den Code vereinfacht und die manuelle Abonnementverwaltung überflüssig macht. Dies verringert außerdem das Risiko, dass der Entwickler vergisst, sich von der Komponente abzumelden, was zu Speicherlecks führen kann. Es ist möglich, die Wahrscheinlichkeit von Speicherlecks mithilfe von Linter-Regeln zu verringern, die darauf abzielen, beobachtbare Objekte zu identifizieren, von denen sie sich nicht abgemeldet haben.
Darüber hinaus führt die Anwendung dieses Ansatzes dazu, dass die Komponenten keine statusbehafteten Komponenten mehr sind, was zu Fehlern führen kann, wenn sich Daten außerhalb des Abonnements ändern.
»Um
 //  <p>{{ textToDisplay }}</p> //  iAmAnObservable   .pipe(      map(value => value.item),      takeUntil(this._destroyed$)    )   .subscribe(item => this.textToDisplay = item); 
▍Nachher
 //  <p>{{ textToDisplay$ | async }}</p> //  this.textToDisplay$ = iAmAnObservable   .pipe(      map(value => value.item)    ); 
6. Abonnements entfernen
takeUntil beim Abonnieren von überwachten Objekten immer sicher, dass Abonnements mit Operatoren wie 
take , 
takeUntil usw. korrekt gelöscht werden.
▍ Erklärungen
Wenn Sie das beobachtete Objekt nicht abbestellen, führt dies zu Speicherverlusten, da der Stream des beobachteten Objekts offen bleibt. Dies ist auch möglich, nachdem die Komponente zerstört wurde oder nachdem der Benutzer eine andere Seite der Anwendung aufgerufen hat.
Noch besser wäre es, eine Linter-Regel zu erstellen, um beobachtete Objekte mit einem gültigen Abonnement zu erkennen.
»Um
 iAmAnObservable   .pipe(      map(value => value.item)        )   .subscribe(item => this.textToDisplay = item); 
▍Nachher
Verwenden Sie den Operator 
takeUntil , wenn Sie die Änderungen eines Objekts beobachten möchten, bis ein anderes beobachtetes Objekt einen bestimmten Wert generiert:
 private destroyed$ = new Subject(); public ngOnInit (): void {   iAmAnObservable   .pipe(      map(value => value.item)      
Die Verwendung von so etwas ist ein Muster, mit dem das Entfernen von Abonnements für viele beobachtete Objekte in einer Komponente gesteuert wird.
Verwenden Sie 
take wenn Sie nur den ersten Wert benötigen, der vom beobachteten Objekt zurückgegeben wird:
 iAmAnObservable   .pipe(      map(value => value.item),      take(1),      takeUntil(this._destroyed$)   )   .subscribe(item => this.textToDisplay = item); 
Bitte beachten Sie, dass wir hier 
takeUntil mit 
take . Dies geschieht, um Speicherverluste zu vermeiden, die dadurch verursacht werden, dass das Abonnement erst nach Zerstörung der Komponente zum Abrufen des Werts führte. Wenn die Funktion 
takeUntil hier nicht verwendet würde, würde das Abonnement bestehen, bis der erste Wert empfangen wurde. Da die Komponente jedoch bereits zerstört worden wäre, wäre dieser Wert niemals empfangen worden, was zu einem Speicherverlust führen würde.
7. Verwenden geeigneter Operatoren
Wenden Sie mithilfe von Glättungsoperatoren mit beobachtbaren Objekten diejenigen an, die den Merkmalen des zu lösenden Problems entsprechen.
- Verwenden Sie switchMapwenn Sie die vorherige geplante Aktion ignorieren müssen, wenn eine neue Aktion eintrifft.
- Verwenden Sie mergeMapfalls Sie allemergeMapAktionen parallel verarbeiten müssen.
- Verwenden Sie concatMapwenn Aktionen nacheinander in der ReihenfolgeconcatMapverarbeitet werden müssen.
- Verwenden Sie exhaustMapin Situationen, in denen Sie bei der Verarbeitung zuvor empfangener Aktionen neue ignorieren müssen.
Details dazu finden Sie 
hier .
▍ Erklärungen
Wenn Sie nach Möglichkeit einen Operator verwenden, anstatt den gleichen Effekt durch Kombinieren mehrerer Operatoren in einer Kette zu erzielen, wird die Menge an Anwendungscode reduziert, die an den Benutzer gesendet werden muss. Die Verwendung eines falsch ausgewählten Operators kann zu einem falschen Programmverhalten führen, da verschiedene Operatoren beobachtete Objekte unterschiedlich verarbeiten.
8. Faules Laden
Versuchen Sie dann, wann immer möglich, das verzögerte Laden von Modulen der Angular-Anwendung zu organisieren. Diese Technik beruht auf der Tatsache, dass etwas nur geladen wird, wenn es verwendet wird. Beispielsweise wird eine Komponente nur geladen, wenn sie angezeigt werden muss.
▍ Erklärungen
Durch verzögertes Laden wird die Größe der Anwendungsmaterialien reduziert, die der Benutzer herunterladen muss. Dies kann die Download-Geschwindigkeit der Anwendung verbessern, da nicht verwendete Module nicht vom Server auf Clients übertragen werden.
»Um
 // app.routing.ts { path: 'not-lazy-loaded', component: NotLazyLoadedComponent } 
▍Nachher
 // 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. Informationen zu Abonnements in anderen Abonnements
Manchmal benötigen Sie zum Ausführen einer Aktion Daten von mehreren beobachtbaren Objekten. Vermeiden Sie in einer solchen Situation, Abonnements für solche Objekte in den 
subscribe anderer beobachtbarer Objekte zu erstellen. Verwenden Sie stattdessen geeignete Operatoren, um Befehle zu verketten. Unter solchen Operatoren kann man 
withLatestFrom und 
combineLatest . Betrachten Sie die Beispiele und kommentieren Sie sie dann.
»Um
 firstObservable$.pipe(  take(1) ) .subscribe(firstValue => {   secondObservable$.pipe(       take(1)   )   .subscribe(secondValue => {       console.log(`Combined values are: ${firstValue} & ${secondValue}`);   }); }); 
▍Nachher
 firstObservable$.pipe(   withLatestFrom(secondObservable$),   first() ) .subscribe(([firstValue, secondValue]) => {   console.log(`Combined values are: ${firstValue} & ${secondValue}`); }); 
▍ Erklärungen
Wenn wir über die Lesbarkeit, die Komplexität des Codes oder die Anzeichen von fehlerhaftem Code sprechen, bedeutet dies, dass der Entwickler die RxJS-API nicht gut kennt, wenn das Programm die RxJS-Funktionen nicht vollständig nutzt. Wenn wir das Thema Leistung 
firstObservable , stellt sich heraus, dass wenn das beobachtete Objekt einige Zeit zum Initialisieren benötigt, es 
firstObservable abonniert, das System auf den Abschluss des Vorgangs wartet und erst danach die Arbeit mit dem zweiten beobachteten Objekt beginnt. Wenn es sich bei diesen Objekten um Netzwerkanforderungen handelt, sieht es wie eine synchrone Ausführung von Anforderungen aus.
10. Über das Tippen
Versuchen Sie immer, Variablen oder Konstanten mit einem anderen Typ als einem anderen zu deklarieren.
▍ Erklärungen
Wenn eine Variable oder Konstante in TypeScript ohne Angabe eines Typs deklariert wird, wird der Typ basierend auf dem ihm zugewiesenen Wert abgeleitet. Dies kann zu Problemen führen. Betrachten Sie ein klassisches Beispiel für das Systemverhalten in einer ähnlichen Situation:
 const x = 1; const y = 'a'; const z = x + y; console.log(`Value of z is: ${z}` //  Value of z is 1a 
Es wird angenommen, dass 
y hier eine Zahl ist, aber unser Programm weiß nichts darüber, sodass etwas angezeigt wird, das falsch aussieht, aber keine Fehlermeldungen erzeugt. Ähnliche Probleme können vermieden werden, indem Variablen und Konstanten geeignete Typen zugewiesen werden.
Wir schreiben das obige Beispiel neu:
 const x: number = 1; const y: number = 'a'; const z: number = x + y; //    : Type '"a"' is not assignable to type 'number'. const y:number 
Dies hilft, Datentypfehler zu vermeiden.
Ein weiterer Vorteil eines systematischen Typisierungsansatzes besteht darin, dass er das Refactoring vereinfacht und die Wahrscheinlichkeit von Fehlern während dieses Prozesses verringert.
Betrachten Sie ein Beispiel:
 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 
Angenommen, wir wollten im 
myFlashObject den Namen der 
loc Eigenschaft in 
location ändern und haben 
myFlashObject Bearbeiten des Codes einen Fehler gemacht:
 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 
Wenn beim Erstellen des 
myFlashObject Objekts keine Eingabe verwendet wird, geht das System in unserem Fall davon aus, dass der Wert der Eigenschaft 
loc von 
myFlashObject undefined . Sie glaubt nicht, dass 
loc ein ungültiger Eigenschaftsname sein könnte.
Wenn bei der Beschreibung des 
myFlashObject Objekts die 
myFlashObject wird, wird in einer ähnlichen Situation beim Kompilieren des Codes eine wunderbare Fehlermeldung angezeigt:
 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}`); } 
Wenn Sie mit der Arbeit an einem neuen Projekt beginnen, ist es hilfreich, in der Datei 
tsconfig.json die Option 
strict:true tsconfig.json , um die strikte Typprüfung zu aktivieren.
11. Über die Verwendung von Linter
Tslint hat verschiedene Standardregeln wie 
No- Any , 
No-Magic- Numbers , 
No-Console . Der Linter kann durch Bearbeiten der Datei 
tslint.json angepasst werden, um die 
tslint.json nach bestimmten Regeln zu organisieren.
▍ Erklärungen
Die Verwendung eines Linter zur Überprüfung des Codes bedeutet, dass Sie eine Fehlermeldung erhalten, wenn in dem Code etwas erscheint, das durch die Regeln verboten ist. Dies trägt zur Einheitlichkeit des Projektcodes bei und verbessert dessen Lesbarkeit. Weitere tslint-Regeln finden Sie hier.
Es ist zu beachten, dass einige Regeln Mittel zur Korrektur dessen enthalten, was als unzulässig angesehen wird. Bei Bedarf können Sie Ihre eigenen Regeln erstellen. Wenn Sie interessiert sind, schauen Sie sich 
dieses Material an, in dem die Erstellung benutzerdefinierter Regeln für den Linter mithilfe von 
TSQuery erläutert wird .
»Um
 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'); }  
▍Nachher
 // 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. 
Zusammenfassung
Heute haben wir 11 Empfehlungen überprüft, von denen wir hoffen, dass sie für Angular-Entwickler nützlich sind. Warten Sie das nächste Mal auf 11 weitere Tipps.
Liebe Leser! Verwenden Sie das Angular-Framework für die Entwicklung von Webprojekten?
