Codeaufteilung auf Routenebene in Angular

Verbessern Sie die Leistung Ihrer App durch Codeaufteilung auf Routenebene.

In diesem Beitrag wird erläutert, wie Sie in einer Angular-Anwendung die Codeaufteilung auf Routenebene einrichten. Dadurch kann die Größe des JavaScript-Bundles reduziert und die Zeit bis Interaktivität erheblich verkürzt werden.

Sie finden die Codebeispiele in diesem Artikel auf GitHub. Das Beispiel für das eager-Routing ist im eager-Branch verfügbar. Das Beispiel für die Codeaufteilung auf Routenebene befindet sich im Lazy-Branch.

Warum Codeaufteilung wichtig ist

Die zunehmende Komplexität von Webanwendungen hat zu einem deutlichen Anstieg der Menge an JavaScript geführt, die an die Nutzer gesendet wird. Große JavaScript-Dateien können die Interaktivität spürbar verzögern und somit insbesondere auf Mobilgeräten kostspielig sein.

Die effizienteste Methode zum Verkleinern von JavaScript-Bundles, ohne auf Funktionen in Ihren Anwendungen zu verzichten, ist die Einführung einer aggressiven Codeaufteilung.

Mit der Codeaufteilung können Sie den JavaScript-Code Ihrer Anwendung in mehrere Blöcke aufteilen, die mit verschiedenen Routen oder Funktionen verknüpft sind. Bei diesem Ansatz wird den Nutzern nur der JavaScript-Code gesendet, den sie beim ersten Laden der Anwendung benötigen. Dadurch bleiben die Ladezeiten kurz.

Verfahren zur Codeaufteilung

Die Codeaufteilung kann auf zwei Ebenen erfolgen: auf Komponentenebene und Routenebene.

  • Bei der Codeaufteilung auf Komponentenebene verschieben Sie Komponenten in ihre eigenen JavaScript-Chunks und laden sie bei Bedarf verzögert.
  • Bei der Codeaufteilung auf Routenebene kapseln Sie die Funktionalität jeder Route in einen separaten Block. Wenn Nutzer durch Ihre Anwendung navigieren, rufen sie die mit den einzelnen Routen verknüpften Blöcke ab und erhalten die zugehörige Funktion bei Bedarf.

In diesem Beitrag geht es um das Einrichten der Aufteilung auf Routenebene in Angular.

Beispielanwendung

Bevor wir uns mit der Codeaufteilung auf Routenebene in Angular befassen, sehen wir uns eine Beispiel-App an:

Sehen Sie sich die Implementierung der Module der App an. Innerhalb von AppModule sind zwei Routen definiert: die Standardroute, die HomeComponent zugeordnet ist, und eine Route nyan, die NyanComponent zugeordnet ist:

@NgModule({
  ...
  imports: [
    BrowserModule,
    RouterModule.forRoot([
      {
        path: '',
        component: HomeComponent,
        pathMatch: 'full'
      },
      {
        path: 'nyan',
        component: NyanComponent
      }
    ])
  ],
  ...
})
export class AppModule {}

Codeaufteilung auf Routenebene

Zum Einrichten der Codeaufteilung muss die eager-Route nyan refaktoriert werden.

Version 8.1.0 der Angular CLI kann alles mit diesem Befehl erledigen:

ng g module nyan --module app --route nyan

Dadurch wird Folgendes generiert: – Ein neues Routingmodul namens NyanModule – Eine Route in AppModule mit dem Namen nyan, die das NyanModule dynamisch lädt – Eine Standardroute im NyanModule – Eine Komponente namens NyanComponent, die gerendert wird, wenn der Nutzer die Standardroute erreicht

Lassen Sie uns diese Schritte manuell durchgehen, um die Implementierung der Codeaufteilung mit Angular besser zu verstehen.

Wenn der Nutzer die Route nyan aufruft, rendert der Router NyanComponent in der Routersteckdose.

Um in Angular die Codeaufteilung auf Routenebene zu verwenden, legen Sie die Eigenschaft loadChildren der Routendeklaration fest und kombinieren Sie sie mit einem dynamischen Import:

{
  path: 'nyan',
  loadChildren: () => import('./nyan/nyan.module').then(m => m.NyanModule)
}

Es gibt zwei wesentliche Unterschiede zur Eifer-Route:

  1. Sie haben loadChildren anstelle von component festgelegt. Bei der Codeaufteilung auf Routenebene müssen Sie auf dynamisch geladene Module und nicht auf Komponenten verweisen.
  2. Sobald das Promise in loadChildren aufgelöst ist, gibst du NyanModule zurück, anstatt auf NyanComponent zu verweisen.

Das obige Snippet gibt an, dass Angular nyan.module dynamisch aus dem Verzeichnis nyan laden und die Komponente rendern soll, die der im Modul deklarierten Standardroute zugeordnet ist, wenn der Nutzer nyan aufruft.

Sie können die Standardroute mithilfe dieser Deklaration mit einer Komponente verknüpfen:

import { NgModule } from '@angular/core';
import { NyanComponent } from './nyan.component';
import { RouterModule } from '@angular/router';

@NgModule({
  declarations: [NyanComponent],
  imports: [
    RouterModule.forChild([{
      path: '',
      pathMatch: 'full',
      component: NyanComponent
    }])
  ]
})
export class NyanModule {}

Mit diesem Code wird NyanComponent gerendert, wenn der Nutzer https://example.com/nyan aufruft.

So prüfen Sie, ob der Angular-Router das nyan.module langsam in Ihre lokale Umgebung herunterlädt:

  1. Drücken Sie Strg + Umschalttaste + J (oder Befehlstaste + Option + J auf dem Mac), um die Entwicklertools zu öffnen.
  2. Klicken Sie auf den Tab Netzwerk.

  3. Klicken Sie in der Beispiel-App auf NYAN.

  4. Die Datei nyan-nyan-module.js wird auf dem Tab „Netzwerk“ angezeigt.

Lazy Loading von JavaScript-Bundles mit Codeaufteilung auf Routingebene

Dieses Beispiel finden Sie auf GitHub.

Rotierendes Ladesymbol anzeigen

Wenn der Nutzer derzeit auf die NYAN-Schaltfläche klickt, zeigt die App nicht an, dass JavaScript im Hintergrund geladen wird. Wenn Sie dem Nutzer beim Laden des Skripts Feedback geben möchten, sollten Sie ein rotierendes Ladesymbol einfügen.

Fügen Sie dazu zuerst Markup für den Indikator innerhalb des router-outlet-Elements in app.component.html hinzu:

<router-outlet>
  <span class="loader" *ngIf="loading"></span>
</router-outlet>

Fügen Sie dann eine AppComponent-Klasse hinzu, um Routingereignisse zu verarbeiten. Diese Klasse setzt das Flag loading auf true, wenn das Ereignis RouteConfigLoadStart erkannt wird, und das Flag auf false, wenn das Ereignis RouteConfigLoadEnd erkannt wird.

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  loading: boolean;
  constructor(router: Router) {
    this.loading = false;
    router.events.subscribe(
      (event: RouterEvent): void => {
        if (event instanceof NavigationStart) {
          this.loading = true;
        } else if (event instanceof NavigationEnd) {
          this.loading = false;
        }
      }
    );
  }
}

Im folgenden Beispiel haben wir eine künstliche Latenz von 500 ms eingeführt, damit Sie das rotierende Ladesymbol in Aktion sehen können.

Fazit

Sie können die Bundle-Größe Ihrer Angular-Anwendungen verkleinern, indem Sie die Codeaufteilung auf Routenebene anwenden:

  1. Mit dem Lazy-Loading-Modulgenerator von Angular CLI können Sie automatisch das Gerüst einer dynamisch geladenen Route erstellen.
  2. Fügen Sie eine Ladeanzeige hinzu, wenn der Nutzer zu einer Lazy Route wechselt, um auf eine laufende Aktion hinzuweisen.