frontend / html
Performance : lazy loading et resource hints niveau expert
Explication
Ce que vous allez apprendre
- Prioriser une ressource critique avec
rel="preload" - Établir une connexion réseau en avance avec
rel="preconnect" - Distinguer précisément
asyncetdefersur une balise<script> - Appliquer
loading="lazy"uniquement là où c'est pertinent, jamais sur l'image LCP - Éliminer le flash de texte invisible (FOIT) avec
font-display: swap
Dans quel contexte ?
Le score Lighthouse Performance de accueil.html stagne à 61/100 à cause d'un LCP (Largest Contentful Paint) trop lent : l'image bannière en haut de page se charge après plusieurs polices et scripts non critiques. En ajoutant <link rel="preload" href="/img/hero.webp" as="image" fetchpriority="high"> dans le <head>, le navigateur télécharge cette image en priorité maximale, avant même de terminer le parsing du reste de la page.
Étape 1 : comment le navigateur charge par défaut
Par défaut, un navigateur télécharge les ressources d'une page dans l'ordre où il les découvre en lisant le HTML. Simple, mais pas toujours optimal.
Étape 2 : le problème concret que ça pose
Une police de caractères critique pour l'affichage du texte, découverte tardivement dans une feuille de style, arrive après des scripts moins urgents. Cette leçon apprend à reprendre la main sur ces priorités.
Étape 3 : le niveau d'urgence maximal, preload
D'abord, le plus fort : preload dit "télécharge ça en priorité maximale, j'en ai besoin très vite". On l'utilise pour une ressource vraiment critique de la page actuelle.
Étape 4 : un niveau plus léger, preconnect
Vient ensuite preconnect, qui dit "établis déjà la connexion réseau vers ce serveur, même si je ne sais pas encore exactement quoi lui demander" — utile pour une police hébergée sur un domaine tiers.
Étape 5 : anticiper le futur, prefetch
Enfin, prefetch anticipe un besoin FUTUR probable, pas immédiat, comme la page suivante que l'utilisateur visitera sans doute.
Étape 6 : la confusion classique entre async et defer
Passons maintenant aux scripts, où une confusion revient sans cesse. defer télécharge le script en parallèle du HTML, mais ne l'exécute qu'une fois le HTML entièrement parsé, dans l'ordre d'apparition — le bon choix pour du code applicatif qui manipule le DOM.
async, lui, télécharge aussi en parallèle, mais exécute le script DÈS qu'il est prêt, sans garantie d'ordre — adapté à du code indépendant comme un script d'analytics.
Attribut <script> | Téléchargement | Exécution | Cas d'usage |
|---|---|---|---|
| (aucun) | Bloque le parsing HTML | Immédiate | À éviter sauf nécessité |
defer | Parallèle | Après le parsing, dans l'ordre | Code applicatif dépendant du DOM |
async | Parallèle | Dès que prêt, ordre non garanti | Analytics, publicité, code indépendant |
Prérequis
Cette leçon suppose une compréhension de base du chargement d'une page (voir la leçon sur la structure HTML) ; elle vise un niveau intermédiaire à expert en optimisation.
Le piège du lazy loading mal placé
Il reste un dernier piège fréquent : appliquer loading="lazy" à TOUTES les images, y compris la plus visible en haut de page (souvent l'élément "LCP" mesuré par Google). C'est contre-productif : cela retarde justement l'image la plus importante pour la perception de vitesse.
Le lazy loading doit cibler uniquement ce qui est hors de l'écran initial.
Piège fréquent
Appliquer loading="lazy" sur l'image bannière visible dès l'ouverture de la page retarde son chargement au lieu de l'accélérer, et pénalise directement le score LCP. Réservez loading="lazy" aux images situées sous la ligne de flottaison (le "pli").
Et ensuite ?
Une fois la performance de chargement maîtrisée, la prochaine leçon aborde une interaction bien différente : le drag & drop natif.
Commandes & code
Performance : lazy loading et resource hints
Réduire le temps avant contenu utile en indiquant au navigateur ce qu'il doit prioriser.
<!-- preload : télécharge en priorité une ressource critique découverte tard -->
<head>
<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/css/critique.css" as="style">
<link rel="preload" href="/img/hero.webp" as="image" fetchpriority="high">
<!-- preconnect : établit la connexion (DNS + TCP + TLS) en avance -->
<link rel="preconnect" href="https://api.technologik.dev">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- dns-prefetch : repli léger pour les domaines tiers secondaires -->
<link rel="dns-prefetch" href="https://cdn.exemple.com">
<!-- prefetch : anticipe une ressource probablement nécessaire bientôt
(page suivante, pas la page courante) -->
<link rel="prefetch" href="/cours/css" as="document">
<!-- modulepreload : équivalent preload pour les modules JS -->
<link rel="modulepreload" href="/js/app.mjs">
</head><!-- Chargement de script : impact direct sur le parsing HTML -->
<script src="/js/analytics.js" async></script>
<!-- async : téléchargé en parallèle, exécuté DÈS qu'il est prêt (ordre non garanti)
-> pour du code indépendant (analytics, ads) -->
<script src="/js/app.js" defer></script>
<!-- defer : téléchargé en parallèle, exécuté APRÈS le parsing HTML, dans l'ordre
-> pour le code applicatif qui dépend du DOM -->
<script src="/js/legacy.js"></script>
<!-- ni async ni defer : bloque le parsing HTML jusqu'à exécution complète
-> à éviter sauf nécessité absolue --><!-- Lazy loading natif : images et iframes hors du viewport initial -->
<img src="/img/photo-1.jpg" alt="Photo 1" loading="lazy">
<img src="/img/photo-2.jpg" alt="Photo 2" loading="lazy">
<!-- MAIS PAS sur l'image la plus visible (souvent le LCP - Largest Contentful Paint) -->
<img src="/img/hero.webp" alt="Bannière principale" fetchpriority="high">
<!-- loading="eager" est la valeur par défaut : pas besoin de le préciser,
fetchpriority="high" suffit à signaler l'importance -->
<iframe src="https://youtube.com/embed/xyz" loading="lazy" title="Vidéo de présentation"></iframe><!-- Fonts : éviter le FOIT (flash of invisible text) -->
<style>
@font-face {
font-family: "Inter";
src: url("/fonts/inter-var.woff2") format("woff2");
font-display: swap; /* affiche le texte en fallback pendant le chargement */
font-weight: 100 900; /* variable font : une seule ressource pour toutes les graisses */
}
</style>
<!-- Critical CSS inline + reste différé -->
<style>
/* CSS critique above-the-fold, injecté directement */
body { margin: 0; font-family: system-ui; }
header { min-height: 64px; }
</style>
<link rel="preload" href="/css/reste.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/css/reste.css"></noscript>| Resource hint | Quand l'utiliser |
|---|---|
preconnect | domaine tiers utilisé très tôt (API, fonts) |
preload | ressource critique de la page actuelle |
prefetch | ressource probable pour la navigation suivante |
modulepreload | modules ES critiques |
Résumé
deferpour le JS applicatif,asyncpour le JS indépendant, jamais les deux ensemble.fetchpriority="high"sur l'image LCP,loading="lazy"sur tout le reste sous le pli.preconnectéconomise une latence complète de handshake sur les domaines tiers critiques.font-display: swapélimine le texte invisible pendant le chargement des polices.
Exercices pratiques
Mission : sauver le score Lighthouse de accueil.html
Objectif : Corriger un LCP trop lent en priorisant l'image bannière, sans commettre l'erreur inverse du lazy loading mal placé.
Contexte
Le score Lighthouse Performance de accueil.html stagne à 61/100 à cause d'un LCP (Largest Contentful Paint) trop lent : l'image bannière hero.webp en haut de page se charge après plusieurs polices et scripts non critiques. Un développeur, pensant bien faire, a même ajouté loading="lazy" sur cette image bannière pour "l'optimiser".
Ta mission : corriger la priorité de chargement de cette image.