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 do Angular, o que pode reduzir o tamanho do pacote JavaScript e melhorar drasticamente o tempo de carregamento.

Você pode encontrar os exemplos de código deste artigo no GitHub. O exemplo de roteamento imediato está disponível na ramificação imediata. 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 significativamente a interatividade, o que pode ser um recurso caro, especialmente em dispositivos móveis.

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

A divisão de código permite dividir o JavaScript do seu aplicativo em vários blocos associados a rotas ou recursos diferentes. 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 do código pode ser feita em dois níveis: o nível do componente e o nível da rota.

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

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

Exemplo de aplicativo

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

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 da rota

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

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 estas etapas manualmente para entender melhor a implementação da 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 importantes em relação à rota ansiosa:

  1. Você definiu 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 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 até https://example.com/nyan.

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

  1. Pressione "Control+Shift+J" (ou "Command+Option+J" no Mac) para abrir as Ferramentas do desenvolvedor.
  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 indicador 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 durante o carregamento do script, provavelmente você vai querer adicionar um indicador de carregamento.

Para fazer isso, comece adicionando 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 vai definir a flag loading como true quando detectar o evento RouteConfigLoadStart e definir a flag 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

É 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 carregados com atraso da CLI do Angular para criar automaticamente uma rota carregada dinamicamente.
  2. Adicione um indicador de carregamento quando o usuário navegar para uma rota lenta para mostrar que há uma ação em andamento.