Nouveau : Datasets open source gratuits disponibles !Decouvrir →
Avance 25 min Angular

Optimiser les performances Angular

Pourquoi Optimiser les performances Angular ?

Dans un environnement moderne et concurrentiel, la performance est devenue une priorité cruciale pour les développeurs. Un site ou une application qui ne répond pas rapidement peut perdre son utilisateur au profit d'un autre plus rapide. L'optimisation des performances en Angular permet non seulement d'améliorer l'expérience utilisateur mais aussi de réduire le temps de chargement et la consommation de ressources du navigateur.

Un cas concret serait une application de gestion de projet où chaque seconde compte pour les responsables qui doivent prendre des décisions rapidement. Optimiser les performances permettrait d'améliorer la productivité globale de l'équipe en offrant des réponses instantanées aux actions des utilisateurs.

Prerequis

  • Connaissance approfondie de Angular 10+.
  • Familiarité avec TypeScript et JavaScript ES6+.
  • Capacité à travailler avec les outils de développement Angular tels que Angular CLI, Webpack, etc.
  • Un environnement de développement propre (Node.js, npm).

Concepts fondamentaux

1. Lazy Loading

Le lazy loading est une technique permettant d'éviter le chargement inutile des composants et des modules non nécessaires lors du démarrage de l'application. Cela réduit la taille initiale de l'application et accélère le temps de chargement.

Schéma mental :

graph TD;
    A[Application] -->|Lazy Loading| B[Module 1];
    A -->|Lazy Loading| C[Module 2];
    B --> D[Component 1];
    B --> E[Component 2];
    C --> F[Component 3];

Code fonctionnel :

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { Module1Component } from './module1/module1.component';
import { Module2Component } from './module2/module2.component';

const routes: Routes = [
  { path: 'module1', loadChildren: () => import('./module1/module1.module').then(m => m.Module1Module) },
  { path: 'module2', loadChildren: () => import('./module2/module2.module').then(m => m.Module2Module) }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

// module1.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Module1Component } from './module1.component';

@NgModule({
  declarations: [Module1Component],
  imports: [CommonModule]
})
export class Module1Module { }

2. OnPush Change Detection Strategy

Angular utilise le changement détection par défaut pour vérifier toutes les propriétés d'un composant à chaque itération du cycle de vie. Cependant, dans certains cas, cela peut entraîner une performance dégradée. L'OnPush change detection strategy permet de restreindre la vérification aux valeurs qui ont réellement changé.

Schéma mental :

graph TD;
    A[Component] --> B[Prop1];
    A --> C[Prop2];

Code fonctionnel :

// app.component.ts
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
  prop1 = 'Value 1';
  prop2 = 'Value 2';
}

3. AOT Compilation

Angular Off-The-Shelf (AOT) est une technique qui compile le code TypeScript en JavaScript avant de l'exécuter dans le navigateur. Cela réduit la taille du bundle final et accélère le temps de démarrage.

Schéma mental :

graph TD;
    A[TypeScript] --> B[AOT Compilation];
    B --> C[JavaScript];

Code fonctionnel :

## Terminal command to build Angular app with AOT
ng build --prod

4. Virtual Scrolling

La virtual scrolling est une technique permettant de rendre des listes ou des tableaux très longs plus performants en affichant uniquement les éléments visibles sur l'écran.

Schéma mental :

graph TD;
    A[List] --> B[Visible Items];

Code fonctionnel :

// app.component.html
<div *ngFor="let item of items | slice: 0:visibleItems; let i = index">
  item
</div>

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  items = Array.from({ length: 1000 }, (_, i) => `Item ${i}`);
  visibleItems = 20;
}

Mise en pratique : projet fil rouge

Étape 1 : Création du projet

## Terminal command to create Angular project
ng new task-manager-app --strict
cd task-manager-app

Étape 2 : Configuration du lazy loading

Créez deux modules dashboard et tasks.

## Terminal commands to generate modules and components
ng generate module dashboard --route=dashboard
ng generate component dashboard/home
ng generate module tasks --route=tasks
ng generate component tasks/list

Étape 3 : Configuration des routes

Mettez à jour le fichier app-routing.module.ts.

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DashboardComponent } from './dashboard/dashboard.component';
import { TasksComponent } from './tasks/tasks.component';

const routes: Routes = [
  { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
  { path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule) },
  { path: 'tasks', loadChildren: () => import('./tasks/tasks.module').then(m => m.TasksModule) }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Étape 4 : Création des composants

Mettez à jour les fichiers dashboard/home.component.ts et tasks/list.component.ts.

// dashboard/home.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent {
  title = 'Welcome to the Task Manager Dashboard';
}
angular
// tasks/list.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-list',
  templateUrl: './list.component.html',
  styleUrls: ['./list.component.css']
})
export class ListComponent implements OnInit {
  tasks = [
    { id: 1, title: 'Task 1', description: 'Description of Task 1' },
    { id: 2, title: 'Task 2', description: 'Description of Task 2' }
    // Add more tasks as needed
  ];

  constructor() { }

  ngOnInit(): void {
  }
}

Étape 5 : Configuration des modules

Mettez à jour les fichiers dashboard/dashboard.module.ts et tasks/tasks.module.ts.

// dashboard/dashboard.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DashboardComponent } from './dashboard.component';
import { RouterModule, Routes } from '@angular/router';

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

@NgModule({
  declarations: [DashboardComponent],
  imports: [CommonModule, RouterModule.forChild(routes)]
})
export class DashboardModule { }
angular
// tasks/tasks.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TasksComponent } from './tasks.component';
import { RouterModule, Routes } from '@angular/router';

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

@NgModule({
  declarations: [TasksComponent],
  imports: [CommonModule, RouterModule.forChild(routes)]
})
export class TasksModule { }

Étape 6 : Ajout des composants au template

Mettez à jour les fichiers app.component.html.

// app.component.html
<nav>
  <a routerLink="/dashboard">Dashboard</a> |
  <a routerLink="/tasks">Tasks</a>
</nav>

<router-outlet></router-outlet>

Étape 7 : Exécution du projet

## Terminal command to serve Angular app
ng serve

Erreurs frequentes et debugging

1. ERROR: Cannot read property 'subscribe' of undefined

Ce message d'erreur peut se produire lorsque vous essayez de vous abonner à un observable qui n'est pas défini.

Code incorrect :

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  data: any;

  constructor(private dataService: DataService) { }

  ngOnInit(): void {
    this.dataService.getData().subscribe(data => {
      this.data = data;
    });
  }
}

Code correct :

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  data: any;

  constructor(private dataService: DataService) { }

  ngOnInit(): void {
    if (this.dataService.getData()) {
      this.dataService.getData().subscribe(data => {
        this.data = data;
      });
    }
  }
}

2. ERROR: Cannot find module './non-existent-module'

Ce message d'erreur indique que le module spécifié n'existe pas.

Code incorrect :

## Terminal command to generate a new component
ng generate component non-existent-module

Correction :

Assurez-vous de saisir le nom du module correctement.

3. ERROR: Expression has changed after it was checked

Ce message d'erreur peut se produire lorsque vous essayez de modifier une propriété après que le changement détecté ait été vérifié.

Code incorrect :

// app.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'App';

  ngOnInit(): void {
    setTimeout(() => {
      this.title = 'Updated Title';
    }, 1000);
  }
}

Code correct :

Utilisez ChangeDetectorRef pour forcer la mise à jour du DOM.

// app.component.ts
import { Component, OnInit, ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'App';

  constructor(private cdr: ChangeDetectorRef) { }

  ngOnInit(): void {
    setTimeout(() => {
      this.title = 'Updated Title';
      this.cdr.detectChanges();
    }, 1000);
  }
}

Pour aller plus loin

  1. Angular Universal : Apprenez à rendre votre application Angular universelle pour une meilleure performances sur les moteurs de recherche et les devices mobiles.

  2. RxJS Best Practices : Explorez des meilleures pratiques pour utiliser RxJS, la bibliothèque reactive JavaScript utilisée par Angular.

  3. AOT Compilation vs JIT Compilation : Comprendre les différences et les avantages de chaque type de compilation pour optimiser vos projets Angular.

Défi pratique

Définissez une fonction debounce personnalisée en TypeScript qui prend un temps d'attente et une fonction à débouncer, puis l'exécute après le délai spécifié.

function debounce<T extends (...args: any[]) => void>(func: T, wait: number): (...args: Parameters<T>) => void {
  let timeout: NodeJS.Timeout | null = null;

  return function(...args: Parameters<T>): void {
    if (timeout) {
      clearTimeout(timeout);
    }

    timeout = setTimeout(() => {
      func.apply(this, args);
    }, wait);
  };
}

Testez cette fonction avec un exemple de recherche en temps réel :

const searchInput = document.getElementById('search-input') as HTMLInputElement;

function handleSearch(query: string): void {
  console.log(`Searching for: ${query}`);
}

const debouncedSearch = debounce(handleSearch, 300);

searchInput.addEventListener('input', (event) => {
  const query = (event.target as HTMLInputElement).value;
  debouncedSearch(query);
});

Cela permet d'éviter les appels fréquents à la fonction de recherche et d'améliorer ainsi les performances.

Besoin d'aide sur Angular ?

Besoin d'aide sur un projet technique ? Decrivez-le pour des conseils personnalises.

Recevoir des conseils

Questions frequentes

Quelles sont les principales techniques pour améliorer le rendu des composants Angular?
Pour améliorer le rendu, on peut utiliser la virtualisation avec des bibliothèques comme ngForOf et trackBy. On peut également optimiser la performance en minifiant et en gzippant les fichiers JavaScript, et en utilisant Webpack pour créer des bundles efficaces.
Comment gérer les performances lors de l'utilisation du lazy loading dans Angular?
Pour utiliser le lazy loading efficacement, on devrait définir des routes qui ne sont chargées que quand elles sont nécessaires. Cela peut être réalisé en utilisant la propriété loadChildren dans le fichier app-routing.module.ts. En outre, il est important de surveiller les performances avec des outils comme Angular DevTools pour s'assurer que l'application charge correctement et sans retard.
Quelles sont les meilleures pratiques pour la gestion de grandes applications Angular?
Pour gérer des grandes applications, il est recommandé d'utiliser un architecture basée sur modules. Chaque fonctionnalité devrait être placée dans son propre module avec ses propres composants, services et routes. Cela rend le code plus organisé et facilite la maintenance. On devrait également utiliser RxJS pour gérer les flux de données et les événements de manière réactive.

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.