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 significativamente o tempo para interação da página.

Confira os exemplos de código deste artigo no GitHub (em inglês). 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 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 enviado aos usuários. Arquivos JavaScript grandes podem atrasar visivelmente a interatividade, por isso, pode ser um recurso caro, especialmente em dispositivos móveis.

A maneira mais eficiente de reduzir pacotes JavaScript sem sacrificar recursos nos seus 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: do componente e da rota.

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

O foco desta postagem é a configuração da divisão no nível da rota no Angular.

Exemplo de aplicativo

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

Confira a implementação dos módulos do app. Dentro de 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 do trajeto

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

A versão 8.1.0 da CLI 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 com o nome nyan que vai carregar dinamicamente a NyanModule - Uma rota padrão no NyanModule - Um componente chamado NyanComponent que vai 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 da 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 da rota rápida:

  1. Você define 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 for resolvida, você vai retornar 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.

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

Para verificar se o roteador do Angular faz o download lento do nyan.module 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

Confira este exemplo no GitHub (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, adicione a marcação para o 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 definirá a sinalização loading como true quando detectar o evento RouteConfigLoadStart e a sinalização 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 conferir o ícone de carregamento em ação.

Conclusão

Você pode reduzir o tamanho do pacote dos seus aplicativos do 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 criar a estrutura automaticamente de uma rota com carregamento dinâmico.
  2. Adicione um indicador de carregamento quando o usuário navegar por uma rota lenta para mostrar que há uma ação em andamento.