Codeaufteilung auf Routenebene in Angular

Mit der Codeaufteilung auf Routingebene lässt sich die Leistung Ihrer App verbessern.

In diesem Beitrag wird beschrieben, wie Sie die Codeaufteilung auf Routingebene in einer Angular-Anwendung einrichten. Dadurch lässt sich die Größe des JavaScript-Bundles reduzieren und die Time to Interactive erheblich verbessern.

Die Codebeispiele aus diesem Artikel finden Sie auf GitHub. Das Beispiel für das vorzeitige Routing ist im Vorab-Branch verfügbar. Das Beispiel für die Code-Spaltung auf Routenebene befindet sich im lazy-Branch.

Warum Code-Splitting wichtig ist

Die ständig wachsende Komplexität von Webanwendungen hat zu einem deutlichen Anstieg der Menge an JavaScript geführt, die an Nutzer gesendet wird. Große JavaScript-Dateien können die Interaktivität erheblich verzögern und sind daher eine kostspielige Ressource, insbesondere auf Mobilgeräten.

Die effizienteste Methode, JavaScript-Bundles zu verkleinern, ohne Funktionen in Ihren Anwendungen zu opfern, ist die aggressive Codeaufteilung.

Mit der Codeaufteilung können Sie das JavaScript Ihrer Anwendung in mehrere Code-Chunks aufteilen, die verschiedenen Routen oder Funktionen zugeordnet sind. Bei diesem Ansatz wird Nutzern nur das JavaScript gesendet, das sie beim ersten Laden der Anwendung benötigen. So bleiben die Ladezeiten niedrig.

Techniken zum Code-Splitting

Das Code-Splitting kann auf zwei Ebenen erfolgen: auf Komponentenebene und auf Wegeebene.

  • Beim Code-Splitting auf Komponentenebene verschieben Sie Komponenten in eigene JavaScript-Chunks und laden sie bei Bedarf träge.
  • Beim Code-Splitting auf Routenebene kapseln Sie die Funktionen jeder Route in einem separaten Chunk ein. Wenn Nutzer durch Ihre Anwendung navigieren, werden die mit den einzelnen Routen verknüpften Chunks abgerufen und die zugehörigen Funktionen werden bei Bedarf abgerufen.

In diesem Beitrag geht es vorrangig um die Einrichtung der Aufteilung auf Routenebene in Angular.

Beispielanwendung

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

Sehen Sie sich die Implementierung der App-Module an. Innerhalb von 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 Routing-Ebene

Um Code Splitting einzurichten, muss die nyan-Eager-Route umgeschrieben werden.

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

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

Dadurch werden folgende Elemente generiert: - Ein neues Routing-Modul namens NyanModule - Eine Route in AppModule namens nyan, über die NyanModule dynamisch geladen wird - Eine Standardroute in NyanModule - Eine Komponente namens NyanComponent, die gerendert wird, wenn der Nutzer die Standardroute aufruft

Gehen wir diese Schritte manuell durch, um das Implementieren des Code-Splittings mit Angular besser zu verstehen.

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

Wenn Sie das Code-Splitting auf Routenebene in Angular verwenden möchten, legen Sie die Property 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 wichtige Unterschiede zum eager-Pfad:

  1. Sie haben loadChildren anstelle von component festgelegt. Wenn Sie die Code-Spaltung auf Routenebene verwenden, müssen Sie auf dynamisch geladene Module statt auf Komponenten verweisen.
  2. In loadChildren geben Sie nach der Auflösung des Versprechens die NyanModule zurück, anstatt auf die NyanComponent zu verweisen.

Im obigen Snippet wird angegeben, dass Angular nyan.module dynamisch aus dem Verzeichnis nyan laden und die Komponente rendern soll, die mit der im Modul deklarierten Standardroute verknüpft ist, wenn der Nutzer zu nyan navigiert.

Sie können die Standardroute mit einer Komponente mithilfe dieser Deklaration 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 wechselt.

So prüfen Sie, ob der Angular-Router die nyan.module in Ihrer lokalen Umgebung verzögert herunterlädt:

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

  3. Klicken Sie in der Beispielanwendung auf NYAN.

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

Lazy-Loading von JavaScript-Bundles mit Code-Splitting auf Routingebene

Dieses Beispiel finden Sie auf GitHub.

Ladesymbol anzeigen

Derzeit gibt die Anwendung nicht an, dass im Hintergrund JavaScript geladen wird, wenn der Nutzer auf die Schaltfläche NYAN klickt. Um dem Nutzer während des Ladens des Scripts Feedback zu geben, sollten Sie wahrscheinlich einen Ladebalken hinzufügen.

Fügen Sie dazu zuerst das Markup für den Indikator in das router-outlet-Element in app.component.html ein:

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

Fügen Sie dann eine AppComponent-Klasse hinzu, um Routing-Ereignisse zu verarbeiten. Diese Klasse setzt das Flag loading auf true, wenn das Ereignis RouteConfigLoadStart erkannt wird, und 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 den Fortschrittsbalken in Aktion sehen können.

Fazit

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

  1. Verwenden Sie den Lazy-Loaded-Modulgenerator der Angular-Befehlszeile, um eine dynamisch geladene Route automatisch zu erstellen.
  2. Fügen Sie eine Ladeanzeige hinzu, wenn der Nutzer zu einer Lazy-Route wechselt, um anzuzeigen, dass eine Aktion ausgeführt wird.