Preloading Strategy de rotas no Angular.

Preloading Strategy de rotas no Angular.

Preloading Strategy no Angular, uma possível solução para seus problemas!

Atualmente o tempo de carregamento de um site, aplicativo ou sistema faz muita diferença para satisfazer o usuário final e evitar até mesmo perdas financeiras. É cada vez mais importante otimizar o tempo de carregamento do aplicativo, pois isso estabelecerá uma primeira impressão positiva. Vamos aprender mais sobre isso neste artigo!

O que é PreloadingStrategy?

Os aplicativos construídos em Angular são modulares e nos permitem construir o frontend em blocos de módulos, que são pequenos contextos que podemos optar por isolar ou compartilhar com outros módulos. Podemos carregar esses módulos lentamente (por demanda) quando o usuário navega para uma rota. Precisamos marcar os módulos para serem carregados lentamente usando a propriedade loadChildren do roteador.

Ao carregar os módulos utilizando Lazy loading, podemos reduzir o tamanho inicial do download do aplicativo e, assim, fazer com que o aplicativo carregue apenas as rotas que o usuário está realmente utilizando.

Isso é muito útil no caso de grandes aplicativos. Mas quando o usuário navega para uma parte de carregamento lento do aplicativo, ou seja que não foi baixada, o angular terá que baixar o módulo do servidor, o que significa que o usuário terá que esperar o download terminar.

Mas ao utilizar o PreloadingStrategy o módulo que está em lazy loading, o usuário não precisa esperar que o módulo seja baixado, pois o módulo já foi baixado em segundo plano.

Ativando o Preloading

Para fazer uso do Pré-carregamento, primeiro precisamos habilitar o carregamento lento dos Módulos. Marque os módulos com o loadChildren, quando você definir rotas como mostrado abaixo. O angular carregará lentamente esses módulos.

const routes: Routes = [
   path: 'conta',
    loadChildren: () => import('./conta/conta.module')
      .then(m => m.ContaModule)
];

Agora sim podemos habilitar o pré-carregamento utilizando o preloadingStrategy: PreloadAllModules, enquanto registra as rotas usando o forRoot método.

RouterModule.forRoot(routes, {preloadingStrategy: PreloadAllModules})

Personalizando a estratégia!

Com PreloadAllModules todos os módulos são pré-carregados, o que pode realmente criar um gargalo caso a aplicação tenha um grande número de módulos a serem carregados. Por isso é importante entender o contexto que a aplicação se encontra e como isso pode afetar o desempenho do site ou app em dispositivos mais lentos, lembre-se que nosso objetivo principal é ter performance. Algumas estratégias para esses casos são:

  • Despejando os módulos necessários na inicialização.
  • Pré-carregar todos os módulos usados ​​com frequência, chamando alguns com um delay.
  • Utilizar apenas o Lazy load em módulos restantes.

Vamos ver como podemos utilizar uma estratégia customizada para Preloading de modules.

Implementando o PreloadingStrategy!

No mundo real, com aplicações que demandam performance, você pode definir um atraso antes de pré-carregar o módulo. Você também pode definir atrasos diferentes para rotas diferentes.

Veja as duas rotas a seguir. Adicionamos dados à rota. As duas rotas têm atrasos diferentes:

const routes: Routes = [
  { path: "test", loadChildren: () => import('./teste/teste.module').then(m => m.TestModule), data: { preload: true, delay: 5000 }  },
  { path: "conta", loadChildren: () => import('./conta/conta.module').then(m => m.ContaModule), data: { preload: true, delay: 7000 } },
];

Agora vamos ver como implementar e customizar o PreloadingStrategy. Dê uma olhada na classe abaixo:

import { Injectable } from '@angular/core';
import { Observable, of, timer } from 'rxjs';
import { map } from 'rxjs/operators'

import { PreloadingStrategy, Route } from '@angular/router';

@Injectable()
export class CustomPreloadingStrategy implements PreloadingStrategy {

  preload(route: Route, loadMe: () => Observable<any>): Observable<any> {

    if (route.data && route.data['preload']) {
      var delay: number = route.data['delay']
      console.log('pre carregamento acionado na rota ' + route.path + ' com um delay de ' + delay);
      return timer(delay).pipe(
        map(_ => {
          console.log("Carregando " + route.path + ' modulo');
          return loadMe();
        }));
    } else {
      console.log('Nenhum pré carregamento ' + route.path);
      return of(null);
    }
  }
}

Como pode ver, implementamos a classe PreloadingStrategy, em seguida, verificamos os dados da rota. Se a pré-carga for verdadeira, verificamos o atraso. E se não for definido route.data ou preload for falso, retornamos o observável de null of(null).

Outra etapa essencial é que precisamos fornecê-lo como provider no AppModule, conforme mostrado abaixo no código:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

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

import { AppComponent } from './app.component';
import { CustomPreloadingStrategy } from './custom-preloading-strategy/custom-preloading-strategy.services';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
  ],
  providers: [CustomPreloadingStrategy],
  bootstrap: [AppComponent]
})
export class AppModule { }

Qual o resultado? Podemos ver abaixo no GIF:

Gravação-de-Tela-2022-04-04-às-19.22.32.gif

Conclusão

Essa é apenas uma pequena dica para desenvolvedores que estão com problemas de performance em casos extremos, onde possuem muitas rotas e muitas são acessadas na aplicação web. Vou disponibilizar o link abaixo do GitHub com o repositório do projeto. Ele está em Angular 12. Fico por aqui e até a próxima! 😄

Repo: github.com/rafaelsmiguelsantos/angular-load..

Referências:

Angular Documentation: angular.io/api/router/PreloadingStrategy

Blog Medium: blog.devgenius.io/advanced-preloading-strat..