Angular: otimize a performance com rotas assíncronas e preloadings

Rocketseat

Navegação Rápida:
Quando uma aplicação Angular começa a ganhar corpo, carregar todo o código de uma vez deixa de fazer sentido. Bundles muito grandes tornam a experiência lenta e a manutenção complicada. É nesse cenário que o lazy loading se destaca. Em vez de entregar tudo já no início, ele divide a aplicação em partes menores e carrega cada pedaço apenas quando é realmente necessário. Isso reduz o tempo de carregamento inicial, economiza banda e melhora a percepção de performance dos usuários.
A ideia é simples: se alguém acessa apenas a página inicial e mais uma seção do app, por que baixar de imediato componentes de rotas que talvez nunca sejam visitadas? O lazy loading resolve essa questão, mantendo o pacote inicial enxuto e carregando rotas e módulos extras sob demanda.
Neste artigo vamos entender como organizar o código em domínios de negócio, configurar rotas assíncronas com a sintaxe moderna do Angular, aplicar estratégias de preloading para otimizar ainda mais e medir o impacto dessas escolhas na prática. Faz sentido? Se sim, bora codar!
Separando por domínios de negócio
Uma boa arquitetura de projeto impulsiona a escalabilidade. Em vez de jogar tudo em um único módulo, vale dividir o app em domínios de negócio, como devs, produtos ou clientes. Cada domínio concentra suas próprias responsabilidades e ganha seus próprios componentes, serviços e rotas. Essa separação ajuda na manutenção, permite que equipes diferentes trabalhem em paralelo e cria o cenário ideal para o lazy loading.
Estrutura de módulos de feature
Uma organização prática é manter cada domínio em uma pasta própria, com seus componentes e arquivo de rotas. Imagine o domínio de desenvolvedores:
src/ └── features/ └── devs/ ├── devs.routes.ts ├── dev-list.component.ts └── dev-detail.component.ts
O arquivo
devs.routes.ts
concentra as rotas do domínio. Assim, cada feature encapsula sua navegação, deixando o projeto mais previsível e simples de evoluir.// features/devs/devs.routes.ts import { Routes } from '@angular/router'; import { DevListComponent } from './dev-list.component'; import { DevDetailComponent } from './dev-detail.component'; export const DEVS_ROUTES: Routes = [ { path: '', component: DevListComponent }, { path: ':id', component: DevDetailComponent } ];
Se os componentes forem standalone, como já é possível nas versões recentes do Angular, basta importá-los no array de rotas. Não é necessário criar um NgModule apenas para isso.
Configurando rotas com lazy loading
Com os domínios organizados, o próximo passo é configurar o roteamento principal para carregar cada parte apenas sob demanda.
Sintaxe moderna com import()
A maneira recomendada é usar
loadChildren
apontando para uma função que retorna uma importação dinâmica. Veja como fica:// app.routes.ts import { Routes } from '@angular/router'; import { HomeComponent } from './home.component'; export const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'devs', loadChildren: () => import('./features/devs/devs.routes').then(m => m.DEVS_ROUTES) } ];
A rota inicial carrega o
HomeComponent
imediatamente. Já o caminho devs
só dispara o download das rotas e componentes quando o usuário de fato acessa essa seção. Isso mantém o bundle inicial menor e evita que o app baixe código desnecessário logo de cara.Rotas filhas no módulo da feature
Dentro do arquivo de rotas do domínio, você pode evoluir a configuração para incluir diferentes páginas relacionadas. Por exemplo:
export const DEVS_ROUTES: Routes = [ { path: '', component: DevListComponent }, // lista de devs { path: 'novo', component: DevFormComponent }, // formulário de criação { path: ':id', component: DevDetailComponent } // detalhe de um dev ];
Essa abordagem mantém a lógica de cada domínio isolada. A equipe que cuida de “devs” não precisa se preocupar com a de “produtos” e vice-versa. Além disso, a navegação fica mais clara e fácil de manter.
Estratégias de preloading
Lazy loading resolve o problema do bundle inicial, mas traz um efeito colateral: na primeira navegação para uma rota lazy, o usuário pode notar um pequeno atraso, já que o Angular precisa baixar o chunk correspondente. Para minimizar esse impacto, existe o preloading.
O preloading antecipa o download de módulos lazy em segundo plano, logo após a inicialização. Assim, quando o usuário acessar essas rotas, o código já estará pronto no cache.
PreloadAllModules
A forma mais direta é habilitar a estratégia PreloadAllModules, que pré-carrega todos os módulos lazy disponíveis. Em projetos configurados com bootstrap standalone, fica assim:
// main.ts import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app.component'; import { routes } from './app.routes'; import { provideRouter, withPreloading, PreloadAllModules } from '@angular/router'; bootstrapApplication(AppComponent, { providers: [provideRouter(routes, withPreloading(PreloadAllModules))] });
Com essa configuração, logo após o app carregar a rota inicial, os demais módulos começam a ser baixados de forma discreta. Isso elimina atrasos posteriores, mas aumenta o consumo de banda. Em apps médios pode ser uma boa solução, mas em sistemas muito grandes pode pesar.
Estratégia customizada
Para ter mais controle, dá para criar uma estratégia seletiva que só pré-carrega o que for marcado com
data: { preload: true }
. Exemplo:// selective-preloading.strategy.ts import { PreloadingStrategy, Route } from '@angular/router'; import { Observable, of } from 'rxjs'; export class SelectivePreloadingStrategy implements PreloadingStrategy { preload(route: Route, load: () => Observable<unknown>): Observable<unknown> { return route.data?.['preload'] ? load() : of(null); } }
E na rota:
{ path: 'devs', loadChildren: () => import('./features/devs/devs.routes').then(m => m.DEVS_ROUTES), data: { preload: true } }
Dessa forma, apenas rotas importantes ou acessadas com frequência são pré-carregadas, equilibrando performance e consumo de rede.
Medindo impacto
Configurar é bom, mas medir é indispensável. Para validar se tudo está funcionando, abra o DevTools do navegador e observe a aba Network. No primeiro carregamento, você verá apenas o bundle inicial. Ao acessar
/devs
, um novo arquivo JavaScript será baixado – o chunk dessa feature. Isso prova que o lazy loading está ativo. Se usar preloading, verá esses chunks sendo baixados logo após a inicialização, sem precisar navegar manualmente.Outra forma é rodar uma auditoria de performance com o Lighthouse. Compare métricas como Largest Contentful Paint (LCP) e Total Blocking Time (TBT) antes e depois da configuração. Normalmente o LCP inicial diminui, já que menos código precisa ser processado para renderizar a tela principal, e o tempo até a interação melhora.
Conclusão e próximos passos
O lazy loading no Angular é uma estratégia poderosa para impulsionar a performance e manter a escalabilidade do projeto. Ao organizar o app em domínios, configurar rotas assíncronas com
loadChildren
, aplicar preloading quando necessário e medir resultados com ferramentas de análise, você constrói aplicações mais rápidas, fluidas e prontas para crescer.Esse ciclo de organizar, otimizar e validar faz parte da jornada de quem busca entregar experiências de qualidade dentro do ecossistema Angular. No próximo passo da série, vamos explorar o HttpClient, que permite integrar dados externos e APIs às nossas rotas. Assim, os módulos lazy não só carregam sob demanda, mas também se conectam a informações dinâmicas.
Se liga: dominar lazy loading é abrir caminho para integrar e escalar sem sacrificar a experiência dos usuários. Bora codar?
Artigos_
Explore conteúdos relacionados
Descubra mais artigos que complementam seu aprendizado e expandem seu conhecimento.