All Articles

Implementando Lazy Loading no Angular

No contexto do Angular um módulo é a forma de agrupar componentes, diretivas, pipes e serviços que são relacionados. Esse conjunto de módulo são “encaixados” para formar a aplicação, isso mesmo como se fosse peças de Lego. Um módulo pode esconder ou exportar um componente, diretiva, serviço ou pipe. Componentes exportados podem ser usados por outros módulos, já aqueles que são escondidos pelo módulo só podem ser usados por ele mesmo.

Módulos Angular

Essa modularidade no Angular é chamada de NgModules, cada aplicação é composta por pelo menos uma classe NgModule que é o módulo root da aplicação. Esse módulo root por padrão é chamado de AppModule e fica no arquivo src/app/app.module.ts. A aplicação é executada através do processo de Bootstraping do módulo root.

Não confundir os módulos Angular com módulos JavaScript

Para definir um módulo Angular devemos usar o decorator de classe @NgModule, esse decorator usa um objeto de metadados com propriedades que definem o módulo. As principais propriedades são:

  • imports: array com outros módulos que são necessários por componentes declarados nesse módulo.
  • declarations: recebe um array de componentes, diretivas e pipes que fazem parte do módulo.
  • exports: define o conjunto de declarações(componentes, pipes etc) que ficam disponíveis para outros módulos.
  • providers: declara os serviços, se for o módulo root, os serviços ficam disponíveis para toda a aplicação.

Exemplo de um módulo root básico:

import { BrowserModule } from '[@angular/platform-browser](http://twitter.com/angular/platform-browser "Twitter profile for @angular/platform-browser")';  
import { NgModule } from '[@angular/core](http://twitter.com/angular/core "Twitter profile for @angular/core")';

import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';

[@NgModule](http://twitter.com/NgModule "Twitter profile for @NgModule")({  
  declarations: \[  
    AppComponent  
  \],  
  imports: \[  
    BrowserModule,  
    AppRoutingModule  
  \],  
  providers: \[\], //Os serviços declarados na propriedade providers do módulo root ficam disponíveis em toda a aplicação  
  bootstrap: \[AppComponent\] //somente o módulo root define um bootstrap  
})  
export class AppModule { }

Nesse código de exemplo tornamos a classe AppModule em um módulo Angular ao definir o decorator @NgModule.

Lazy Loading

Uma vantagem de dividir a aplicação em módulos é a possibilidade de fazer o carregamento de determinados módulos somente quando houver necessidade. Quando se usa módulos Lazy Loading, o carregamento só é feito quando o usuário navega para a rota do respectivo módulo.

Vamos ver o funcionamento do Lazy Loading na prática, usando o @angular/cli vamos criar uma nova aplicação:

ng new lazy-app —routing

Com esse comando criamos toda a estrutura da aplicação, o parâmetro “routing” faz com que o @angular/cli também crie o arquivo de rotas da aplicação.

Edite o template app.component.html para incluir os links de navegação na página inicial:

<nav>  
  <a routerLink="home">Home Component</a>  
  <a routerLink="lazy">Lazy Module</a>  
</nav>

<router-outlet></router-outlet>

Como nossa aplicação não tem um estilo definido, vamos somente aumentar a fonte e adicionar um espaçamento entre os links de navegação editando o arquivo app.component.css:

a {      
    text-align: center;  
    padding: 14px;      
    font-size: 17px;      
}

Vamos criar o componente Home que será carregado na inicialização e chamado pela rota padrão:

ng generate component home

Após a criação do componente Home vamos adicionar as rotas no arquivo app-routing.module.ts:

import { NgModule } from '[@angular/core](http://twitter.com/angular/core "Twitter profile for @angular/core")';  
import { Routes, RouterModule } from '[@angular/router](http://twitter.com/angular/router "Twitter profile for @angular/router")';  
import { HomeComponent } from './home/home.component';

const routes: Routes = \[  
  **{path: '', redirectTo: 'home', pathMatch: 'full'},  
  {path: 'home', component: HomeComponent}**  
\];

[@NgModule](http://twitter.com/NgModule "Twitter profile for @NgModule")({  
  imports: \[RouterModule.forRoot(routes)\],  
  exports: \[RouterModule\]  
})  
export class AppRoutingModule { }

A primeira definição redireciona a rota padrão para a rota home, a segunda faz a chamada do componente Home. Com essas alterações após o Hot Reload o componente Home será carregado na página inicial:

Criando o Lazy Module

Vamos criar um novo módulo chamado Lazy:

ng generate module lazy —routing

O @angular/cli gera o novo módulo e seu respectivo arquivo de rotas:

Em seguida vamos criar um componente que fará parte no módulo Lazy. Vamos navegar até a pasta do módulo e executar o comando de criação de componente:

cd src/app/lazy  
ng generate component sobre  

Estando dentro da pasta do módulo ao executar o comando faz com que o @angular/cli importe automaticamente o componente criado no arquivo lazy.module.ts:

import { NgModule } from '[@angular/core](http://twitter.com/angular/core "Twitter profile for @angular/core")';  
import { CommonModule } from '[@angular/common](http://twitter.com/angular/common "Twitter profile for @angular/common")';

import { LazyRoutingModule } from './lazy-routing.module';  
import { SobreComponent } from './sobre/sobre.component';

[@NgModule](http://twitter.com/NgModule "Twitter profile for @NgModule")({  
  imports: \[  
    CommonModule,  
    LazyRoutingModule  
  \],  
 ** declarations: \[SobreComponent\]**  
})  
export class LazyModule { }

O novo componente criado foi importado automaticamente pelo @angular/cli na propriedade declarations do módulo.

O próximo passo é editar o arquivo app-routing.module.ts para adicionar as rota para o módulo:

import { NgModule } from '[@angular/core](http://twitter.com/angular/core "Twitter profile for @angular/core")';  
import { Routes, RouterModule } from '[@angular/router](http://twitter.com/angular/router "Twitter profile for @angular/router")';  
import { HomeComponent } from './home/home.component';

const routes: Routes = \[  
  {path: '', redirectTo: 'home', pathMatch: 'full'},  
  {path: 'home', component: HomeComponent},  
  **{path: 'lazy', loadChildren: './lazy/lazy.module#LazyModule'}**  
\];

[@NgModule](http://twitter.com/NgModule "Twitter profile for @NgModule")({  
  imports: \[RouterModule.forRoot(routes)\],  
  exports: \[RouterModule\]  
})  
export class AppRoutingModule { }

Criamos uma nova rota chamada lazy, que usa a propriedade loadChildren para fazer o carregamento do módulo. Dessa forma o módulo Lazy somente será carregado quando a rota lazy for acionada pelo usuário.

Para carregar o componente Sobre que se encontra dentro do módulo Lazy devemos editar o arquivo de rotas do módulo lazy-routing.module.ts:

import { NgModule } from '[@angular/core](http://twitter.com/angular/core "Twitter profile for @angular/core")';  
import { Routes, RouterModule } from '[@angular/router](http://twitter.com/angular/router "Twitter profile for @angular/router")';  
import { SobreComponent } from './sobre/sobre.component';

const routes: Routes = \[  
  {path: '', component: SobreComponent}  
\];

[@NgModule](http://twitter.com/NgModule "Twitter profile for @NgModule")({  
  imports: \[RouterModule.forChild(routes)\],  
  exports: \[RouterModule\]  
})  
export class LazyRoutingModule { }

Essa rota define que quando o módulo for carregado a rota padrão dele vai apontar para o componente Sobre que será carregado:

Como vemos na imagem, ao clicar no link “Lazy Module” é feito o carregamento do módulo Lazy que em seguida chama o componente Sobre. Na aba network do Chrome DevTools podemos verificar os arquivos que são carregados:

E ter certeza que o módulo Lazy só é carregado após clicar no link:

Definitivamente a divisão de uma aplicação em módulos e o uso do Lazy Loading é uma ótima alternativa para melhorar o tempo de carregamento e o tamanho do arquivo inicial da aplicação, pois no processo de Bootstraping somente o módulo root será carregado.

O código completo desse exemplo está disponível no Github.