Replay

Apprendre Angular en 1h

Je me donne un objectif: vous faire découvrir et apprendre Angular en 1 heure: composant, syntaxe dans les templates, les directives, les signaux, les routeurs, les services, l'injection de dépendances, les observables et les requêtes HTTP. Le nécessaire pour faire une application Angular !.

Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

Optimiser vos images avec NgOptimizedImage dans Angular

Les images constituent souvent la majeure partie du poids d'une page web et peuvent considérablement affecter les performances de votre application. La directive NgOptimizedImage d'Angular est conçue pour simplifier l'adoption des meilleures pratiques en matière de chargement d'images, améliorant ainsi l'expérience utilisateur et les performances de votre site.

Imaginez que vous avez créé un magnifique site e-commerce avec de nombreuses images de produits. Sans optimisation, vos utilisateurs risquent de quitter votre site avant même que les images ne soient chargées, frustré par les temps d'attente. C'est comme si vous aviez une vitrine magnifique, mais que les clients ne pouvaient pas la voir à cause d'un rideau qui s'ouvre trop lentement.

Qu'est-ce que NgOptimizedImage ?

NgOptimizedImage est une directive qui optimise automatiquement le chargement des images en appliquant plusieurs techniques d'optimisation, notamment :

  • Priorisation du chargement de l'image LCP (Largest Contentful Paint)
  • Chargement paresseux (lazy loading) des images non critiques
  • Préconnexion automatique aux domaines d'images
  • Génération automatique d'attributs srcset pour les images responsives
  • Préchargement des images importantes lors du rendu côté serveur

NOTE TECHNIQUE

Priorisation du chargement de l'image LCP (Largest Contentful Paint) : Le LCP est une métrique de performance qui mesure le temps nécessaire pour afficher le plus grand élément visible dans la fenêtre d'affichage initiale (souvent une image). En priorisant cette image, on améliore considérablement la perception de vitesse de chargement par l'utilisateur.

Chargement paresseux (lazy loading) : Technique qui retarde le chargement des images non visibles dans la fenêtre d'affichage initiale jusqu'à ce que l'utilisateur fasse défiler la page et s'en approche. Cela permet de réduire le temps de chargement initial et d'économiser la bande passante.

Préconnexion automatique aux domaines d'images : Établit à l'avance les connexions nécessaires aux serveurs hébergeant les images, éliminant ainsi le temps de négociation DNS, TLS et TCP lors du chargement réel des images.

Génération automatique d'attributs srcset : Permet de fournir plusieurs versions d'une même image à différentes résolutions, laissant le navigateur choisir la plus adaptée selon la taille d'écran et la densité de pixels, optimisant ainsi le rapport qualité/performance.

Préchargement des images importantes : Lors du rendu côté serveur (SSR), les balises de préchargement sont ajoutées au HTML pour que le navigateur commence à télécharger les images critiques avant même que le JavaScript ne soit chargé et exécuté.

Comment l'implémenter dans votre projet

1. Importer la directive

Commençons par importer la directive NgOptimizedImage depuis @angular/common :

ts
import { NgOptimizedImage } from '@angular/common';

Ensuite, incluez-la dans le tableau imports de votre composant autonome ou de votre module :

ts
imports: [
  NgOptimizedImage,
  // autres imports
],

2. Activer la directive

Pour activer la directive NgOptimizedImage, remplacez l'attribut src de vos images par ngSrc :

html
<img ngSrc="mon-image.jpg" width="400" height="200">

IMPORTANT

Vous devez toujours spécifier les attributs width et height pour éviter les décalages de mise en page (layout shifts).

3. Marquer les images prioritaires

La directive permet d'identifier les images critiques qui doivent être chargées en priorité, notamment celle qui constitue le LCP (Largest Contentful Paint) de votre page :

html
<img ngSrc="image-hero.jpg" width="800" height="400" priority>

L'attribut priority applique plusieurs optimisations :

  • Définit fetchpriority=high pour indiquer au navigateur de prioriser cette ressource
  • Définit loading=eager pour éviter le chargement paresseux
  • Génère automatiquement un élément link preload lors du rendu côté serveur

CONSEIL

Utilisez l'outil Chrome DevTools pour identifier votre image LCP. Dans l'onglet Performance, cliquez sur "Démarrer le profilage et recharger la page", puis sélectionnez "LCP" dans la section temporelle.

Mode de remplissage (fill mode)

Parfois, vous souhaitez qu'une image remplisse complètement son conteneur parent, comme pour une image d'arrière-plan. Le mode fill est conçu pour ce scénario :

html
<img ngSrc="fond.jpg" fill>

Avec l'attribut fill, vous n'avez pas besoin de spécifier width et height. Vous pouvez ensuite utiliser la propriété CSS object-fit pour contrôler comment l'image remplit son conteneur :

css
img {
  object-fit: cover; /* L'image couvre tout l'espace, peut être recadrée */
}

Ou pour maintenir le ratio d'aspect et éviter le recadrage :

css
img {
  object-fit: contain; /* L'image est visible en entier, avec des "letterbox" si nécessaire */
}

PRÉREQUIS

Pour que le mode fill fonctionne correctement, l'élément parent de l'image doit avoir un style position: "relative", position: "fixed", ou position: "absolute".

Migrer depuis background-image

Si vous utilisez actuellement des images d'arrière-plan avec background-image en CSS, voici comment migrer vers NgOptimizedImage :

  1. Supprimez la propriété background-image de l'élément conteneur
  2. Assurez-vous que le conteneur a position: "relative", position: "fixed", ou position: "absolute"
  3. Créez un nouvel élément image en tant qu'enfant du conteneur, en utilisant ngSrc
  4. Ajoutez l'attribut fill à cet élément
  5. Si cette image pourrait être votre élément LCP, ajoutez l'attribut priority

Utilisation des placeholders

La directive NgOptimizedImage permet d'afficher des images de placeholder pendant le chargement de l'image principale :

html
<img ngSrc="produit.jpg" width="400" height="200" placeholder>

L'attribut placeholder demande automatiquement une version plus petite de l'image qui sera utilisée comme fond pendant le chargement de l'image principale.

Configuration d'un loader d'image

Les loaders d'images permettent d'optimiser davantage vos images en utilisant des services de CDN d'images. Angular fournit des loaders préconfigurés pour plusieurs services populaires :

  • Cloudflare
  • Cloudinary
  • ImageKit
  • Imgix
  • Netlify

Pour configurer un loader, ajoutez la factory du provider correspondant dans le tableau providers :

ts
providers: [
  provideImgixLoader('https://mon-site.imgix.net/'),
],

Vous pouvez également créer votre propre loader personnalisé si nécessaire :

ts
providers: [
  {
    provide: IMAGE_LOADER,
    useValue: (config: ImageLoaderConfig) => {
      return `https://mon-cdn.exemple.com/images?src=${config.src}&width=${config.width}`;
    },
  },
],

Création d'un composant d'image optimisée

Créons un composant réutilisable qui tire parti de NgOptimizedImage pour optimiser toutes les images de notre application :

ts
import { Component } from '@angular/core';
import { NgOptimizedImage } from '@angular/common';
import { input } from '@angular/core';

@Component({
  selector: 'app-optimized-image',
  standalone: true,
  imports: [NgOptimizedImage],
  template: `
    <img
      [ngSrc]="src()"
      [width]="width()"
      [height]="height()"
      [priority]="priority()"
      [fill]="fill()"
      [placeholder]="placeholder() ? 'blur' : undefined"
    />
  `,
  styles: [`
    :host {
      display: block;
      position: relative;
    }
    img[fill] {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
  `]
})
export class OptimizedImageComponent {
  /**
   * Chemin de l'image à afficher
   * @example "assets/images/product.jpg"
   */
  src = input.required<string>();
  
  /**
   * Largeur de l'image en pixels (requise sauf si fill est true)
   * @example 400
   */
  width = input<number>();
  
  /**
   * Hauteur de l'image en pixels (requise sauf si fill est true)
   * @example 300
   */
  height = input<number>();
  
  /**
   * Indique si l'image est prioritaire (LCP)
   * @example true
   */
  priority = input(false);
  
  /**
   * Indique si l'image doit remplir son conteneur
   * @example true
   */
  fill = input(false);
  
  /**
   * Indique si une image de placeholder doit être affichée pendant le chargement
   * @example true
   */
  placeholder = input(false);
}

Cas d'utilisation pratique

Dans une application de gestion d'utilisateurs, nous pouvons utiliser ce composant pour afficher les avatars des utilisateurs de manière optimisée :

Configuration avec un CDN d'images

Pour une optimisation encore plus poussée, configurons notre application pour utiliser Imgix comme CDN d'images :

ts
// app.config.ts
import { provideHttpClient } from '@angular/common/http';
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideImgixLoader } from '@angular/common';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient(),
    provideImgixLoader('https://mon-site.imgix.net/')
  ]
};

AVANTAGES DU CDN

L'utilisation d'un CDN d'images permet de :

  • Redimensionner automatiquement les images selon les besoins
  • Convertir les images dans des formats modernes (WebP, AVIF)
  • Compresser les images sans perte de qualité perceptible
  • Servir les images depuis des serveurs géographiquement proches des utilisateurs

Erreurs courantes et solutions

Lors de l'utilisation de NgOptimizedImage, vous pourriez rencontrer certaines erreurs. Voici un guide pour les résoudre :

ErreurExplicationSolution
NG02963: The NgOptimizedImage directive has detected that the 'placeholder' attribute is set to true but no image loader is configuredCette erreur se produit lorsque vous utilisez l'attribut placeholder sans avoir configuré de loader d'image. Le placeholder nécessite un loader pour générer la version basse résolution de l'image.Configurez un loader d'image dans le tableau providers de votre application :
providers: [provideImgixLoader('https://votre-compte.imgix.net/')],
NG02962: The NgOptimizedImage directive (activated on an <img> element with the 'ngSrc="..."') has detected that width and height attributes are missingVous avez oublié de spécifier les dimensions de l'image, ce qui peut causer des décalages de mise en page.Ajoutez les attributs width et height à votre balise <img> :
html<br><img ngSrc="image.jpg" width="400" height="300">
Ou utilisez l'attribut fill si l'image doit remplir son conteneur.
NG02965: The NgOptimizedImage directive (activated on an <img> element with the 'ngSrc="..."') has detected that the 'fill' attribute is present but the CSS position property is not set to "relative", "fixed", or "absolute"L'attribut fill nécessite que le conteneur parent ait un positionnement spécifique.Ajoutez position: relative (ou absolute/fixed) au conteneur parent de l'image :
css.image-container { position: relative;}
NG02964: The NgOptimizedImage directive (activated on an <img> element with the 'ngSrc="..."') has detected that the 'priority' attribute is used on an image that is not detected as an LCP elementVous avez marqué une image comme prioritaire alors qu'elle n'est probablement pas l'élément LCP principal.Réservez l'attribut priority uniquement pour les images importantes visibles dans la fenêtre d'affichage initiale, généralement les grandes images en haut de la page.
NG02967: The NgOptimizedImage directive (activated on an <img> element with the 'ngSrc="..."') has detected that this image is loading without 'priority', but it's loading eagerlyL'image est chargée de manière eager (non paresseuse) mais n'est pas marquée comme prioritaire.Soit ajoutez l'attribut priority si l'image est importante, soit supprimez loading="eager" pour permettre le chargement paresseux automatique.

ATTENTION AUX LOADERS

Si vous utilisez l'attribut placeholder ou si vous souhaitez générer des versions responsives de vos images, un loader d'image est obligatoire. Sans loader, certaines fonctionnalités de NgOptimizedImage ne fonctionneront pas correctement.