Suddivisione del codice a livello di route in Angular

Migliora le prestazioni della tua app utilizzando la suddivisione del codice a livello di percorso.

Questo post spiega come configurare la suddivisione del codice a livello di route in un'applicazione Angular, che può ridurre le dimensioni del bundle JavaScript e migliorare notevolmente il tempo di interattività.

Puoi trovare gli esempi di codice di questo articolo su GitHub. L'esempio di routing eager è disponibile nel ramo eager. L'esempio di suddivisione del codice a livello di route si trova nel ramo lento.

Perché la suddivisione del codice è importante

La sempre crescente complessità delle applicazioni web ha portato a un aumento significativo della quantità di JavaScript spedito agli utenti. I file JavaScript di grandi dimensioni possono ritardare notevolmente l'interattività, quindi possono rappresentare una risorsa costosa, soprattutto sui dispositivi mobili.

Il modo più efficiente per ridurre i bundle JavaScript senza sacrificare le funzionalità delle applicazioni è introdurre una suddivisione aggressiva del codice.

La suddivisione del codice ti consente di dividere il codice JavaScript dell'applicazione in più blocchi associati a route o funzionalità diverse. Questo approccio invia agli utenti solo il codice JavaScript di cui hanno bisogno durante il caricamento iniziale dell'applicazione, riducendo i tempi di caricamento.

Tecniche di suddivisione del codice

La suddivisione del codice può essere effettuata a due livelli: a livello di componente e a livello di percorso.

  • Nella suddivisione del codice a livello di componente, puoi spostare i componenti nei rispettivi blocchi JavaScript e caricarli lentamente quando sono necessari.
  • Nella suddivisione del codice a livello di route, la funzionalità di ogni route viene incapsulata in un blocco separato. Quando gli utenti navigano nell'applicazione, recuperano i blocchi associati alle singole route e ricevono la funzionalità associata quando ne hanno bisogno.

Questo post è incentrato sulla configurazione della suddivisione a livello di percorso in Angular.

Applicazione di esempio

Prima di vedere come utilizzare la suddivisione del codice a livello di percorso in Angular, diamo un'occhiata a un'app di esempio:

Controlla l'implementazione dei moduli dell'app. All'interno di AppModule sono definite due route: la route predefinita associata a HomeComponent e una route nyan associata a NyanComponent:

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

Suddivisione del codice a livello di route

Per configurare la suddivisione del codice, è necessario eseguire il refactoring del percorso eager nyan.

La versione 8.1.0 dell'interfaccia a riga di comando Angular può fare tutto per te con questo comando:

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

Verrà generato: - Un nuovo modulo di routing denominato NyanModule - Una route in AppModule denominata nyan che caricherà dinamicamente NyanModule - Una route predefinita in NyanModule - Un componente denominato NyanComponent che verrà sottoposto al rendering quando l'utente seleziona la route predefinita

Seguiamo questi passaggi manualmente per capire meglio come implementare la suddivisione del codice con Angular.

Quando l'utente raggiunge la route nyan, il router eseguirà il rendering del NyanComponent nella presa del router.

Per utilizzare la suddivisione del codice a livello di route in Angular, imposta la proprietà loadChildren della dichiarazione di route e combinala con un'importazione dinamica:

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

Ci sono due differenze fondamentali rispetto al percorso più entusiasta:

  1. Hai impostato loadChildren anziché component. Quando utilizzi la suddivisione del codice a livello di route, devi puntare ai moduli caricati dinamicamente, anziché ai componenti.
  2. In loadChildren, una volta risolta la promessa, restituisci NyanModule anziché puntare a NyanComponent.

Lo snippet riportato sopra specifica che, quando l'utente passa a nyan, Angular deve caricare dinamicamente nyan.module dalla directory nyan ed eseguire il rendering del componente associato alla route predefinita dichiarata nel modulo.

Puoi associare la route predefinita a un componente utilizzando questa dichiarazione:

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

Questo codice esegue il rendering di NyanComponent quando l'utente va alla pagina https://example.com/nyan.

Per verificare che il router Angular scarichi lentamente nyan.module nel tuo ambiente locale:

  1. Premi "Control+Maiusc+J" (o "Comando+Opzione+J" su Mac) per aprire DevTools.
  2. Fai clic sulla scheda Rete.

  3. Fai clic su NYAN nell'app di esempio.

  4. Tieni presente che il file nyan-nyan-module.js viene visualizzato nella scheda di rete.

Caricamento lento di bundle JavaScript con suddivisione del codice a livello di route

Trova questo esempio su GitHub.

Mostra una rotellina

Al momento, quando l'utente fa clic sul pulsante NYAN, l'applicazione non indica che sta caricando JavaScript in background. Per dare un feedback all'utente durante il caricamento dello script, dovrai aggiungere una rotellina.

Per farlo, inizia aggiungendo il markup per l'indicatore all'interno dell'elemento router-outlet in app.component.html:

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

Quindi aggiungi una classe AppComponent per gestire gli eventi di routing. Questa classe imposterà il flag loading su true quando sentirà l'evento RouteConfigLoadStart e imposterà il flag su false quando sentirà l'evento 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;
        }
      }
    );
  }
}

Nell'esempio riportato di seguito abbiamo introdotto una latenza artificiale di 500 ms in modo da poter vedere la rotellina in azione.

Conclusione

Puoi ridurre le dimensioni del bundle delle applicazioni Angular applicando la suddivisione del codice a livello di route:

  1. Usa il generatore di moduli con caricamento lento dell'interfaccia a riga di comando Angular per creare automaticamente lo scaffold di una route caricata dinamicamente.
  2. Aggiungi un indicatore di caricamento quando l'utente accede a un percorso lento per mostrare che è in corso un'azione.