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.

Esta postagem explica 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 do JavaScript e melhorar muito o tempo para interação da página.

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 do código no nível da rota está na ramificação lenta.

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. Grandes arquivos JavaScript podem atrasar visivelmente a interatividade, portanto, pode ser um recurso caro, especialmente em dispositivos móveis.

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

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: do componente e da rota.

  • Na divisão de código em nível de componente, você move os componentes para os próprios blocos JavaScript e os carrega lentamente quando são necessários.
  • 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 no aplicativo, eles buscam os blocos associados às rotas individuais e obtêm a funcionalidade associada quando necessário.

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

Exemplo de aplicativo

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

Conferir a implementação dos módulos do app. Dentro de AppModule, duas rotas são definidas: a padrão associada a HomeComponent e uma 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 rápida 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 conferir 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 à rota antecipada:

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

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

Você pode 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 para https://example.com/nyan.

Para verificar se o roteador Angular faz o download do nyan.module lentamente 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. Observe que 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 (link em inglês).

Mostrar um ícone de carregamento

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

Para fazer isso, comece adicionando uma marcação para o indicador dentro do elemento router-outlet no 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 ouvir o evento RouteConfigLoadStart e a sinalização como false quando o evento RouteConfigLoadEnd detectar.

@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 Angular para criar a estrutura automaticamente (scaffolding) de uma rota carregada dinamicamente.
  2. Adicione um indicador de carregamento quando o usuário navegar para um trajeto lento para mostrar que há uma ação em andamento.