Divisão de código no nível da rota no Angular

Melhore o desempenho do seu app usando a divisão de código no nível da rota.

Nesta postagem, explicamos como configurar a divisão de código no nível da rota em um aplicativo Angular, o que pode reduzir o tamanho do pacote JavaScript e melhorar drasticamente o Time to Interactive.

Os exemplos de código deste artigo estão disponíveis no GitHub. O exemplo de roteamento antecipado está disponível na ramificação eager. O exemplo de divisão de código no nível da rota está na ramificação lazy.

Por que a divisão de código é importante

A complexidade cada vez maior dos aplicativos da Web levou a um aumento significativo na quantidade de JavaScript enviada aos usuários. Arquivos JavaScript grandes podem atrasar significativamente a interatividade, então podem ser um recurso caro, especialmente em dispositivos móveis.

A maneira mais eficiente de reduzir os pacotes de JavaScript sem sacrificar os recursos dos aplicativos é introduzir uma divisão de código agressiva.

A divisão de código permite dividir o JavaScript do aplicativo em vários blocos associados a diferentes rotas ou recursos. Essa abordagem envia aos usuários apenas o JavaScript necessário durante o carregamento inicial do aplicativo, mantendo os tempos de carregamento baixos.

Técnicas de divisão de código

A divisão de código pode ser feita em dois níveis: componente e rota.

  • Na divisão de código no nível do componente, você move os componentes para os próprios blocos de JavaScript e os carrega de forma lenta quando necessário.
  • Na divisão de código no nível da rota, você encapsula a funcionalidade de cada rota em um bloco separado. Quando os usuários navegam pelo aplicativo, eles buscam os blocos associados às rotas individuais e recebem a funcionalidade associada quando precisam dela.

Este post se concentra na configuração da divisão no nível da rota no Angular.

Exemplo de aplicativo

Antes de entender como usar a divisão de código no nível da rota no Angular, vamos conferir um app de exemplo:

Confira a implementação dos módulos do app. Em AppModule, duas rotas são definidas: a rota padrão associada a HomeComponent e uma rota nyan associada a NyanComponent:

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

Divisão de código no nível da rota

Para configurar a divisão de código, a rota antecipada nyan precisa ser refatorada.

A versão 8.1.0 da CLI do Angular pode fazer tudo por você com este comando:

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

Isso vai gerar: - Um novo módulo de roteamento chamado NyanModule - Uma rota em AppModule chamada nyan que vai carregar dinamicamente o NyanModule - Uma rota padrão no NyanModule - Um componente chamado NyanComponent que será renderizado quando o usuário acessar a rota padrão

Vamos seguir essas etapas manualmente para entender melhor como implementar a divisão de código com o Angular.

Quando o usuário navega para a rota nyan, o roteador renderiza o NyanComponent na saída do roteador.

Para usar a divisão de código no nível da rota no Angular, defina a propriedade loadChildren da declaração de rota e combine-a com uma importação dinâmica:

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

Há duas diferenças principais em relação ao caminho antecipado:

  1. Você define loadChildren em vez de component. Ao usar a divisão de código no nível da rota, é necessário apontar para módulos carregados dinamicamente, em vez de componentes.
  2. Em loadChildren, depois que a promessa é resolvida, você retorna o NyanModule em vez de apontar para o NyanComponent.

O snippet acima especifica que, quando o usuário navegar até nyan, o Angular vai carregar dinamicamente nyan.module do diretório nyan e renderizar o componente associado à rota padrão declarada no módulo.

É possível associar a rota padrão a um componente usando esta declaração:

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

Esse código renderiza NyanComponent quando o usuário navega até https://example.com/nyan.

Para verificar se o roteador do Angular faz o download do nyan.module de forma lenta no ambiente local:

  1. Pressione "Control+Shift+J" (ou "Command+Option+J" no Mac) para abrir o DevTools.
  2. Clique na guia Rede.

  3. Clique em NYAN no app de exemplo.

  4. O arquivo nyan-nyan-module.js aparece na guia de rede.

Carregamento lento de pacotes JavaScript com divisão de código no nível da rota

Encontre este exemplo no GitHub.

Mostrar um ícone de carregamento

No momento, quando o usuário clica no botão NYAN, o aplicativo não indica que está carregando o JavaScript em segundo plano. Para dar feedback ao usuário enquanto o script é carregado, adicione um ícone de carregamento.

Para fazer isso, comece adicionando a marcação do indicador dentro do elemento router-outlet em app.component.html:

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

Em seguida, adicione uma classe AppComponent para processar eventos de roteamento. Essa classe vai definir a flag loading como true quando detectar o evento RouteConfigLoadStart e como false quando detectar o 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;
        }
      }
    );
  }
}

No exemplo abaixo, introduzimos uma latência artificial de 500 ms para que você possa ver o ícone de carregamento em ação.

Conclusão

É possível reduzir o tamanho do pacote dos seus aplicativos Angular aplicando a divisão de código no nível da rota:

  1. Use o gerador de módulos de carregamento lento da CLI do Angular para estruturar automaticamente uma rota carregada de forma dinâmica.
  2. Adicione um indicador de carregamento quando o usuário navegar para uma rota lenta para mostrar que há uma ação em andamento.