Esc
 Naviguer  Ouvrir Esc Fermer
Aller au contenu

2.4 — Anatomie d'un navigateur

🎯 Objectif : comprendre les étapes par lesquelles passe une page HTML pour devenir une interface visible et interactive, et savoir où interviennent les optimisations de performance.

À l'issue de cet axe, tu sauras :

  • Décrire le pipeline de rendu : parsing → DOM → CSSOM → render tree → layout → paint → composite
  • Comprendre l'event loop, les microtasks et les macrotasks
  • Distinguer reflow et repaint, savoir ce qui les déclenche
  • Identifier ce qu'apportent Service Workers et PWA
  • Mesurer le coût d'une animation et la rendre fluide à 60 FPS

Débutant 10 min prérequis : axe 1 lu

Quand le navigateur reçoit du HTML, il suit ce parcours, en parallèle quand c’est possible :

flowchart LR
    HTML[HTML reçu] --> ParseHTML[Parsing HTML]
    ParseHTML --> DOM[DOM]
    CSS[CSS reçu] --> ParseCSS[Parsing CSS]
    ParseCSS --> CSSOM[CSSOM]
    DOM --> RT[Render Tree]
    CSSOM --> RT
    RT --> Layout[Layout<br/>position + taille]
    Layout --> Paint[Paint<br/>pixels par couche]
    Paint --> Composite[Composite<br/>assemblage GPU]
    Composite --> Ecran[Pixels à l'écran]
Le pipeline critique d'affichage

Le parseur transforme le HTML en arbre d’objets : le DOM. Chaque balise devient un nœud.

<!DOCTYPE html>
<html>
<head><title>Hello</title></head>
<body>
<h1>Bonjour</h1>
<p>Texte</p>
</body>
</html>

devient :

Document
└─ html
├─ head
│ └─ title — "Hello"
└─ body
├─ h1 — "Bonjour"
└─ p — "Texte"

Pareil pour les CSS : un arbre CSS Object Model (CSSOM).

Combinaison DOM + CSSOM, sans les éléments invisibles (display: none, <head>, balises sans contenu visuel).

Calcul de la position et de la taille de chaque nœud (en cascade depuis la racine, en respectant les règles CSS : flex, grid, flow).

Le navigateur dessine les pixels de chaque nœud sur des couches (layers) en mémoire.

Le GPU assemble les couches en une image finale. Cette étape peut être très rapide pour des animations de transform ou opacity.

Modifier le DOM ou le CSS ne déclenche pas toujours tout le pipeline.

ModifÉtapes affectéesCoût
Changer transform ou opacitycomposite uniquement🟢 ~ instantané
Changer color, background-colorpaint + composite🟡 modéré
Changer width, height, toplayout + paint + composite🔴 cher
Insérer/supprimer un élémentlayout + paint + composite🔴 cher

Conséquence : pour animer fluide à 60 FPS (16 ms par frame), privilégie transform et opacity. Évite d’animer width, height, top.

/* ❌ Animation chère — déclenche layout */
.box { transition: top 0.3s; }
.box.move { top: 100px; }
/* ✅ Animation cheap — composite seul */
.box { transition: transform 0.3s; }
.box.move { transform: translateY(100px); }

Le navigateur ne fait pas que rendre — il exécute aussi du JS. Un seul thread (le main thread) le fait, via une boucle d’événements.

flowchart TD
    Start[Boucle]
    Start --> Task[Prendre 1 macrotask<br/>setTimeout, événement DOM, fetch...]
    Task --> Micro[Vider toutes les microtasks<br/>Promesses, queueMicrotask, MutationObserver]
    Micro --> Render{Rendu nécessaire ?}
    Render -->|oui| RAF[requestAnimationFrame<br/>+ layout + paint + composite]
    Render -->|non| Start
    RAF --> Start
Event loop : tâches, microtasks, render
TypeQui les poseQuand ils s’exécutent
MacrotasksetTimeout, événements DOM, fetch qui revientUne à la fois entre deux tours de boucle
MicrotaskPromise.then, await, queueMicrotask, MutationObserverToutes vidées avant la prochaine macrotask
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// Output : 1, 4, 3, 2
// 1 et 4 sont synchrones
// 3 est une microtask (vidée avant le prochain tour)
// 2 est une macrotask (au tour suivant)

Avant de retéléchargér une ressource, le navigateur vérifie son cache local.

HeaderEffet
Cache-Control: max-age=3600Frais pendant 1 h, pas de requête
Cache-Control: no-cacheToujours revalider (mais peut servir si 304)
Cache-Control: no-storeNe jamais cacher (pages sensibles)
Cache-Control: immutableNe jamais revalider (assets versionnés)
ETag: "abc123"Empreinte de la ressource pour If-None-Match
Client : GET /style.css
If-None-Match: "abc123"
Serveur : 304 Not Modified (corps vide, le client réutilise le cache)
Type d’assetStratégie
HTMLCache-Control: no-cache (toujours revalider)
Assets versionnés (app.a3f4e1.js)Cache-Control: public, max-age=31536000, immutable
Images, policesCache long avec ETag
API JSONSelon le cas — souvent no-store ou caching applicatif

Un Service Worker est un script qui tourne en arrière-plan, en dehors de la page, et qui peut intercepter les requêtes réseau.

flowchart LR
    Page[Page web<br/>main thread] -->|fetch| SW[Service Worker]
    SW -->|cache hit| Cache[(Cache API)]
    SW -->|cache miss| Net[Réseau]
    Net --> SW
    Cache --> SW
    SW --> Page
Le Service Worker comme proxy de l'application

Capacités :

  • Mode hors-ligne : servir une version cachée si le réseau est absent.
  • Stratégies de cache : cache-first, network-first, stale-while-revalidate.
  • Notifications push.
  • Background sync : envoyer des données quand le réseau revient.

Une PWA = un site web qui :

  • a un Service Worker (offline + perf)
  • a un manifest.json (nom, icônes)
  • est servi en HTTPS
  • est installable (icône sur l’écran d’accueil)

C’est une alternative légère aux apps natives, idéale pour les outils internes ou les sites qu’on veut « presque comme une app ».

manifest.json
{
"name": "Mon App",
"short_name": "MonApp",
"start_url": "/",
"display": "standalone",
"theme_color": "#2563eb",
"icons": [
{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}

Trois métriques Core Web Vitals que Google et tout le monde surveillent :

MétriqueMesureCible « bonne »
LCP (Largest Contentful Paint)Temps avant que le plus gros élément visible soit affiché< 2.5 s
INP (Interaction to Next Paint)Latence entre clic/tape et rendu visible< 200 ms
CLS (Cumulative Layout Shift)Ampleur des sauts de layout pendant le chargement< 0.1

Outils : Lighthouse (Chrome DevTools), PageSpeed Insights, WebPageTest. On approfondit dans l’axe 13.

Tu animes un menu en modifiant width: 0 → width: 300px. L'animation est saccadée. Pourquoi ?
Quel ordre d'exécution est correct ? console.log('A'); setTimeout(() => console.log('B'), 0); Promise.resolve().then(() => console.log('C'));
Tu sers une grosse application avec des assets versionnés (app.a3f4.js). Quelle stratégie de cache pour ces fichiers ?
  • Inside look at modern web browser — Mariko Kosaka (4 articles, Google) : la référence visuelle
  • web.dev — Rendering performance : web.dev/articles/rendering-performance
  • JavaScript Visualized: Event Loop — Lydia Hallie (vidéo + article)
  • PWA on web.dev : web.dev/learn/pwa

Fin de l’axe 2. Direction l’axe 3 — Analyse & conception, ou attaque l’exercice mini-curl.