Kodokon kodokon.com

Pipeline de rendu : reflow, repaint, compositing

Comprenez les quatre phases du rendu navigateur pour savoir précisément quelles propriétés CSS coûtent cher et comment will-change agit sous le capot.

11 min · 3 questions

Ouvrir cette leçon dans Kodokon

Le navigateur transforme votre CSS en pixels via un pipeline en quatre phases : Style (calcul des valeurs), Layout (géométrie, aussi appelé reflow), Paint (rasterisation en pixels, le repaint) et Composite (assemblage des calques sur le GPU). La règle d'or : plus vous touchez une phase en amont, plus la facture est lourde, car toutes les phases suivantes sont rejouées. Modifier width ou top déclenche layout + paint + composite. Modifier color ou box-shadow saute le layout mais repeint. Seules transform et opacity peuvent être traitées par le thread compositeur seul : l'animation reste fluide même si le thread principal est bloqué par du JavaScript.

CSS
.slide-bad {
  position: absolute;
  transition: left 0.3s ease;
}
.slide-bad:hover { left: 120px; }

.slide-good {
  transition: transform 0.3s ease;
}
.slide-good:hover { transform: translateX(120px); }
Deux animations visuellement identiques : la première force un reflow à chaque frame, la seconde reste sur le compositeur.

Le navigateur est paresseux : il regroupe vos écritures de style et ne recalcule le layout qu'au prochain rafraîchissement. Mais certaines lectures JavaScript - offsetHeight, getBoundingClientRect(), scrollTop, getComputedStyle() sur une propriété géométrique - exigent des valeurs à jour. Si une écriture est en attente, le navigateur doit recalculer immédiatement et de façon synchrone : c'est le reflow forcé. Alterner lecture et écriture dans une boucle produit du layout thrashing : un reflow complet par itération au lieu d'un seul pour tout le lot.

JAVASCRIPT
const items = document.querySelectorAll('.item');

items.forEach((item) => {
  const h = item.offsetHeight;
  item.style.height = h * 2 + 'px';
});

const heights = [...items].map((i) => i.offsetHeight);
items.forEach((item, index) => {
  item.style.height = heights[index] * 2 + 'px';
});
Version 1 : un reflow forcé par élément. Version 2 : toutes les lectures d'abord, puis toutes les écritures - un seul reflow.

Le compositing découpe la page en calques rasterisés indépendamment et assemblés par le GPU. will-change: transform demande la promotion de l'élément en calque dédié avant l'animation, évitant la rasterisation coûteuse au premier frame. Subtilité de spécification souvent ignorée : will-change doit produire les mêmes effets de bord que la propriété annoncée. Ainsi will-change: transform crée un contexte d'empilement et transforme l'élément en bloc conteneur pour ses descendants en position: fixed - exactement comme un vrai transform. Un simple hint de performance peut donc casser votre empilement ou vos éléments fixes.

JAVASCRIPT
const panel = document.querySelector('.panel');

panel.addEventListener('pointerenter', () => {
  panel.style.willChange = 'transform';
});

panel.addEventListener('transitionend', () => {
  panel.style.willChange = 'auto';
});
Le pattern recommandé par la spécification : promouvoir juste avant l'animation, libérer juste après.

Quiz de validation

Vérifiez que vous avez bien retenu les points clés de cette leçon.

  1. Quelle animation peut être entièrement gérée par le thread compositeur, sans reflow ni repaint ?
    • height de 0 à 200px
    • margin-left de 0 à 100px
    • transform: translateX(100px)
    • box-shadow au survol
  2. Pourquoi lire offsetHeight dans une boucle qui modifie aussi les styles est-il si coûteux ?
    • Chaque lecture force un reflow synchrone si une écriture est en attente
    • offsetHeight déclenche un repaint GPU à chaque accès
    • La propriété n'est jamais mise en cache par le moteur JavaScript
  3. Quel effet de bord will-change: transform partage-t-il avec un vrai transform ?
    • Il désactive les transitions CSS de l'élément
    • Il crée un contexte d'empilement et un bloc conteneur pour position: fixed
    • Il force l'élément en display: block
    • Il double la résolution de rasterisation