Lazy Loading von Feature-Modulen mit Angular

Blog_Lazy_Loading

Die Performanz einer Web-Anwendung spielt stets eine wichtige Rolle. In diesem Artikel soll es darum gehen das initiale Laden der Anwendung so schnell wie möglich zu gestalten. Denn kein Anwender wartet gerne auf eine Anwendung bis diese endlich geladen ist.

Natürlich gibt es einige Möglichkeiten die Performanz einer Anwendung zu verbessern. Dieser Artikel gibt einen guten Überblick über die verschiedenen Aspekte, die eine Rolle spielen. Thema dieses Artikels ist das Lazy Loading von Angular Modulen.

Eine Sache noch vorweg. Der gesamte Quellcode ist auf Github zu finden. Einfach auschecken und schon habt ihr ein lauffähiges Beispiel.


Grundsätzliches 

Wenn ich von Angular Modulen rede, dann sind immer die Klassen gemeint, die mit dem @NgModules Dekorator beschriftet sind. Ein solches Modul kann beliebig viele Angular-Artefakte enthalten (Komponenten, Services, Pipes, Directives, ...). Ratsam ist es immer nicht nur ein großes Modul pro Anwendung zu definieren, sondern die Anwendung in mehrere Module zu schneiden. Eine klare und saubere Strukturierung des Quellcodes hat wie bekannt unzählige Vorteile. Im Angular Umfeld ist der Rahmen eines Moduls häufig eine Route. Bedeutet konkret, dass es unser Ziel ist einzelne Module unserer Anwendung zu definierten Zeitpunkten nachzuladen. Durch den späteren Zeitpunkt des Ladens der verschiedenen Module haben wir den Vorteil, dass der initiale Ladevorgang weniger JavaScript lädt und deswegen schneller vonstatten geht.

Der Einfachheit halber fangen wir im nachfolgenden Beispiel auf der grünen Wiese an.

Das Beispiel 

Grüne Wiese bedeutet, dass wir ein neues Angular Projekt anlegen. In diesem Projekt werden wir ein weiteres Modul erzeugen, welches per Lazy Loading nicht beim initialen Aufruf der Anwendung geladen wird. Sondern eben erst später.
Das Anlegen des Projektes erfolgt mit dem nächsten Befehl. Wichtig ist die Angabe von --routing. Dies ist die erste Voraussetzung für den Einsatz von Lazy Loading. Dadurch entsteht neben dem AppModule ein weiteres Modul mit dem Namen AppRoutingModule. Im AppRoutingModule werden alle Routen der Anwendung konfiguriert. Das AppModule​ ist das Haupt-Modul der Anwendung in dem auch das AppRoutingModule​ aufgenommen wird. Wir schauen uns beide Module gleich noch genauer an.
ng new angular-lazy-loading --routing 
Das Projekt ist nun vorbereitet. Als nächstes erstellen wir das angesprochene Modul, welches in der Anwendung per Lazy Loading nachgeladen werden soll. Dieser Befehl muss im Projektverzeichnis ausgeführt werden.
ng generate module lazy-loaded --route lazy-loaded-route --module app.module 

Das erzeugte Modul hat den Namen LazyLoadedModule. Die Route wird in der URL über den Namen lazy-loaded-route erreichbar sein. Durch die --module Angabe wird das neue Modul dem AppRoutingModule bekanntgemacht. Schauen wir uns als erstes das AppModule an.


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { } 
Eine explizite Routing-Definition ist hier noch nicht zu finden. Durch die Angabe von --routing bei der Erzeugung des Projektes wird die Routing-Konfiguration durch AppRoutingModule erledigt. Dieses Modul schauen wir uns als nächstes an.
const routes: Routes = [
  {
    path: 'lazy-loaded-route',
    loadChildren: () =>
      import('./lazy-loaded/lazy-loaded.module').then(m => m.LazyLoadedModule)
  }
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {} 

Hier im AppRoutingModule ist das Modul LazyLoadedModule zu finden. Der ng generate module Befehl hat automatisch die Route so definiert, dass diese per Lazy Loading nachgeladen werden kann. Wir müssen also nichts weiter dafür tun, um einen Nachlade-Mechanismus zu implementieren.

Im Prinzip war's das schon! Als letzten Schritt passen wir noch die app.component.html Datei an. Hier fügen wir eine Verlinkung zur lazy-loaded-route hinzu, damit man zwischen den Routen wechseln kann.

Inhalt der app.component.html:

<h1>
  {{title}}
</h1>

<button routerLink="/lazy-loaded-route">Lazy Loaded Route</button>
<button routerLink="">Home</button>

<router-outlet></router-outlet>
 
Nun kann die Anwendung gestartet werden. Dies erfolgt mit dem Befehl ng serve. Danach kann im Browser die Seite unter http://localhost:4200 aufgerufen werden. Mit der Entwicklerkonsole eures Lieblings-Browsers (Chrome oder Firefox - alles andere kann ich mir nicht vorstellen), könnt ihr genau sehen, dass das notwendige JavaScript für das LazyLoadedModule erst zu einem späteren Zeitpunkt nachgeladen wird. Der erste Screenshot zeigt den Browser und die Entwicklerkonsole nach dem Aufruf der Seite.
Wenn man nun auf den Button zur lazy-loaded-route klickt, kann man sehr gut sehen, dass ein weiterer Request gesendet wurde, um die JavaScript Dateien der Route zu laden.

Ziel erreicht 

​Das Werk ist vollbracht. Erst wenn die Route betreten wird, werden die notwendigen Applikationsdateien der Route geladen.

Wir haben somit erreicht, dass das kompilierte JavaScript des LazyLoadedModule nicht Teil des initialen Ladevorgangs der Anwendung ist. Das zu ladene JavaScript, um die Seite aufzurufen, ist also kleiner, als wenn wir nur ein einzelnes großes Modul definiert hätten.

Ende 

Noch einmal der Hinweis: Das Beispiel ist natürlich auf Github zu finden. So habt ihr gleich ein laufendes Beispiel, welches ihr euch nochmal im Detail anschauen könnt. Viel Spaß und Happy Hacking!

By accepting you will be accessing a service provided by a third-party external to https://blog.ordix.de/