Codeaufteilung auf Routenebene in Angular

Verbessern Sie die Leistung Ihrer App, indem Sie Code-Splitting auf Routenebene verwenden.

In diesem Beitrag wird erläutert, wie Sie in einer Angular-Anwendung Code-Splitting auf Routenebene einrichten. Dadurch kann die Größe des JavaScript-Bundles reduziert und die Time to Interactive erheblich verbessert werden.

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

Warum Codeaufteilung wichtig ist

Die immer komplexer werdenden Webanwendungen haben dazu geführt, dass die Menge an JavaScript, die an Nutzer gesendet wird, erheblich zugenommen hat. Große JavaScript-Dateien können die Interaktivität erheblich verzögern. Daher kann es sich um eine kostspielige Ressource handeln, insbesondere auf Mobilgeräten.

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

Mit Codeaufteilung können Sie das JavaScript Ihrer Anwendung in mehrere Teile aufteilen, die mit verschiedenen Routen oder Funktionen verknüpft sind. Bei diesem Ansatz wird nur das JavaScript gesendet, das beim ersten Laden der Anwendung benötigt wird. Dadurch bleiben die Ladezeiten gering.

Techniken zur Codeaufteilung

Die Codeaufteilung kann auf zwei Ebenen erfolgen: auf der Komponentenebene und auf der Route-Ebene.

  • Beim Code-Splitting auf Komponentenebene verschieben Sie Komponenten in eigene JavaScript-Chunks und laden sie bei Bedarf verzögert.
  • Beim Code-Splitting auf Routenebene wird die Funktionalität jeder Route in einem separaten Chunk gekapselt. Wenn Nutzer in Ihrer Anwendung navigieren, werden die mit den einzelnen Routen verknüpften Chunks abgerufen und die zugehörigen Funktionen bei Bedarf bereitgestellt.

In diesem Beitrag geht es um das Einrichten von Codeaufteilung auf Routenebene in Angular.

Beispielanwendung

Bevor wir uns damit befassen, wie Sie die Codeaufteilung auf Routenebene in Angular verwenden, sehen wir uns eine Beispiel-App an:

Sehen Sie sich die Implementierung der Module der App an. In AppModule sind zwei Routen definiert: die Standardroute, die mit HomeComponent verknüpft ist, und eine nyan-Route, die mit NyanComponent verknüpft ist:

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

Codeaufteilung auf Routenebene

Um die Codeaufteilung einzurichten, muss die Eager-Route nyan refaktoriert werden.

Mit der Version 8.1.0 der Angular-Befehlszeile können Sie mit diesem Befehl alles erledigen:

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

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

Gehen wir diese Schritte manuell durch, um die Implementierung der Codeaufteilung mit Angular besser zu verstehen.

Wenn der Nutzer die Route nyan aufruft, rendert der Router die NyanComponent im Router-Outlet.

Wenn Sie die Codeaufteilung auf Routenebene in Angular verwenden möchten, legen Sie das Attribut loadChildren der Routendeklaration fest und kombinieren Sie es mit einem dynamischen Import:

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

Es gibt zwei wesentliche Unterschiede zur Eager-Route:

  1. Sie legen loadChildren anstelle von component fest. Wenn Sie die Codeaufteilung auf Routenebene verwenden, müssen Sie auf dynamisch geladene Module statt auf Komponenten verweisen.
  2. In loadChildren geben Sie nach der Auflösung des Promise das NyanModule zurück, anstatt auf das NyanComponent zu verweisen.

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

Mit dieser Deklaration können Sie die Standardroute 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 zu https://example.com/nyan gelangt.

So prüfen Sie, ob der Angular-Router die nyan.module in Ihrer lokalen Umgebung per Lazy Loading herunterlädt:

  1. Drücken Sie „Strg + Umschalttaste + J“ (oder „Befehlstaste + Wahltaste + J“ auf einem 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 Routenebene

Dieses Beispiel finden Sie auf GitHub.

Spinner anzeigen

Wenn der Nutzer auf den Button NYAN klickt, wird in der Anwendung nicht angezeigt, dass JavaScript im Hintergrund geladen wird. Damit der Nutzer während des Ladens des Scripts Feedback erhält, sollten Sie wahrscheinlich einen Spinner hinzufügen.

Fügen Sie dazu zuerst das Markup für die Anzeige im router-outlet-Element 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 sie das Ereignis RouteConfigLoadStart empfängt, und auf false, wenn sie das Ereignis RouteConfigLoadEnd empfängt.

@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 Beispiel unten haben wir eine künstliche Latenz von 500 ms eingeführt, damit Sie den Spinner in Aktion sehen können.

Fazit

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

  1. Verwenden Sie den Generator für Lazy-Loading-Module der Angular-Befehlszeile, um automatisch ein Gerüst für eine dynamisch geladene Route zu erstellen.
  2. Fügen Sie eine Ladeanzeige hinzu, wenn der Nutzer zu einer Lazy-Route navigiert, um zu zeigen, dass eine Aktion ausgeführt wird.