Fractionnement du code au niveau de la route dans Angular

Améliorez les performances de votre application en utilisant la division du code au niveau des routes.

Cet article explique comment configurer la division du code au niveau des routes dans une application Angular, ce qui peut réduire la taille du bundle JavaScript et améliorer considérablement le Time to Interactive.

Vous trouverez les exemples de code de cet article sur GitHub. L'exemple de routage hâtif est disponible dans la branche eager. L'exemple de division du code au niveau de la route se trouve dans la branche lazy.

Pourquoi la division du code est importante

La complexité croissante des applications Web a entraîné une augmentation significative de la quantité de code JavaScript envoyé aux utilisateurs. Les fichiers JavaScript volumineux peuvent retarder considérablement l'interactivité. Ils peuvent donc être coûteux, en particulier sur mobile.

Le moyen le plus efficace de réduire la taille des bundles JavaScript sans sacrifier les fonctionnalités de vos applications consiste à introduire une division agressive du code.

La division du code vous permet de diviser le code JavaScript de votre application en plusieurs fragments associés à différentes routes ou fonctionnalités. Cette approche n'envoie aux utilisateurs que le code JavaScript dont ils ont besoin lors du chargement initial de l'application, ce qui permet de réduire les temps de chargement.

Techniques de fractionnement du code

La division du code peut être effectuée à deux niveaux : au niveau du composant et au niveau de la route.

  • Dans la division du code au niveau des composants, vous déplacez les composants vers leurs propres fragments JavaScript et les chargez de manière différée lorsqu'ils sont nécessaires.
  • Dans la division du code au niveau de la route, vous encapsulez la fonctionnalité de chaque route dans un fragment distinct. Lorsque les utilisateurs parcourent votre application, ils récupèrent les fragments associés aux différentes routes et obtiennent la fonctionnalité associée lorsqu'ils en ont besoin.

Cet article explique comment configurer la division au niveau des routes dans Angular.

Exemple d'application

Avant de découvrir comment utiliser la division du code au niveau de la route dans Angular, examinons un exemple d'application :

Examinez l'implémentation des modules de l'application. Deux routes sont définies dans AppModule : la route par défaut associée à HomeComponent et une route nyan associée à NyanComponent :

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

Division du code au niveau de la route

Pour configurer la division du code, la route hâtive nyan doit être refactorisée.

La version 8.1.0 de la CLI Angular peut tout faire pour vous avec cette commande :

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

Cela générera : - Un nouveau module de routage appelé NyanModule - Une route dans AppModule appelée nyan qui chargera dynamiquement le NyanModule - Une route par défaut dans le NyanModule - Un composant appelé NyanComponent qui sera affiché lorsque l'utilisateur accèdera à la route par défaut

Suivons ces étapes manuellement pour mieux comprendre comment implémenter la division du code avec Angular.

Lorsque l'utilisateur accède à la route nyan, le routeur affiche NyanComponent dans la sortie du routeur.

Pour utiliser la division du code au niveau de la route dans Angular, définissez la propriété loadChildren de la déclaration de route et combinez-la avec une importation dynamique :

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

Il existe deux différences majeures par rapport à la route hâtive :

  1. Vous avez défini loadChildren au lieu de component. Lorsque vous utilisez la division du code au niveau de la route, vous devez pointer vers des modules chargés dynamiquement, et non vers des composants.
  2. Dans loadChildren, une fois la promesse résolue, vous renvoyez NyanModule au lieu de pointer vers NyanComponent.

L'extrait ci-dessus spécifie que lorsque l'utilisateur accède à nyan, Angular doit charger dynamiquement nyan.module à partir du répertoire nyan et afficher le composant associé à la route par défaut déclarée dans le module.

Vous pouvez associer la route par défaut à un composant à l'aide de cette déclaration :

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 {}

Ce code affiche NyanComponent lorsque l'utilisateur accède à https://example.com/nyan.

Pour vérifier que le routeur Angular télécharge nyan.module de manière différée dans votre environnement local :

  1. Appuyez sur Ctrl+Maj+J (ou Cmd+Option+J sur Mac) pour ouvrir les outils de développement.
  2. Cliquez sur l'onglet Réseau.

  3. Cliquez sur NYAN dans l'application exemple.

  4. Notez que le fichier nyan-nyan-module.js apparaît dans l'onglet "Réseau".

Chargement différé des bundles JavaScript avec fractionnement du code au niveau de la route

Retrouvez cet exemple sur GitHub.

Afficher un icône de chargement

Pour le moment, lorsque l'utilisateur clique sur le bouton NYAN, l'application n'indique pas qu'elle charge du code JavaScript en arrière-plan. Pour donner un retour à l'utilisateur pendant le chargement du script, vous pouvez ajouter un indicateur de chargement.

Pour ce faire, commencez par ajouter le balisage de l'indicateur dans l'élément router-outlet de app.component.html :

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

Ajoutez ensuite une classe AppComponent pour gérer les événements de routage. Cette classe définit l'indicateur loading sur true lorsqu'elle entend l'événement RouteConfigLoadStart et sur false lorsqu'elle entend l'événement RouteConfigLoadEnd.

@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;
        }
      }
    );
  }
}

Dans l'exemple ci-dessous, nous avons introduit une latence artificielle de 500 ms pour que vous puissiez voir le spinner en action.

Conclusion

Vous pouvez réduire la taille du bundle de vos applications Angular en appliquant la division du code au niveau des routes :

  1. Utilisez le générateur de modules à chargement différé de la CLI Angular pour créer automatiquement un échafaudage pour une route chargée dynamiquement.
  2. Ajoutez un indicateur de chargement lorsque l'utilisateur accède à une route chargée de manière différée pour indiquer qu'une action est en cours.