Appearance
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
:
- Supprimez la propriété
background-image
de l'élément conteneur - Assurez-vous que le conteneur a
position: "relative"
,position: "fixed"
, ouposition: "absolute"
- Créez un nouvel élément image en tant qu'enfant du conteneur, en utilisant
ngSrc
- Ajoutez l'attribut
fill
à cet élément - 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 :
Erreur | Explication | Solution |
---|---|---|
NG02963: The NgOptimizedImage directive has detected that the 'placeholder' attribute is set to true but no image loader is configured | Cette 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 missing | Vous 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 element | Vous 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 eagerly | L'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.