Gestion du budget de préchargement d’images via Service Worker 2025 — Priorisation et INP maîtrisé
Publié: 29 sept. 2025 · Temps de lecture: 5 min · Par la rédaction Unified Image Tools
De nombreuses équipes ajoutent du préchargement d’images pour améliorer le LCP et se retrouvent avec un Service Worker qui monopolise la bande passante et détériore l’INP. En 2025, Priority Hints est stabilisé et le Network Information API fournit des signaux plus fiables, ce qui permet de moduler le préchargement dynamiquement. Cet article traite la sélection d’assets et leur timing comme un « budget », et montre comment anticiper les héros ou galeries sans sacrifier l’expérience utilisateur.
TL;DR
- Quantifier le budget : calculer
budget = (downlink × 0,25) - ressources LCP en cours
, puis interrompre le préchargement quand le résultat devient négatif. - Reclasser côté Service Worker : collecter Navigation Timing + télémétrie INP et ajuster automatiquement le rang pour la prochaine visite.
- Relier Priority Hints à
fetchpriority
: écraser la valeur HTML (low
) depuis le Service Worker et basculer versauto
/high
selon le contexte. - Réessayer avec Background Sync : annuler en connexion limitée/offline et replanifier via
periodicSync
la nuit. - Observabilité : tracer taux de succès et ΔLCP dans performance-guardian pour vérifier que le budget reste pertinent.
Concevoir le modèle de budget
Métrique | Collecte | Cadence conseillée | Objectif |
---|---|---|---|
downlink | navigator.connection.downlink | Début de session & changements réseau | Estimation de bande passante |
effectiveType | Network Information API | À chaque exécution | Qualification 3G/4G/5G |
inpP75 | PerformanceObserver + RUM | À chaque exécution | Déclencher si l’INP se dégrade |
lcpCandidateSize | performance.getEntriesByType('largest-contentful-paint') | Quand le LCP se fige | Mesurer la taille de la ressource LCP |
prefetchSuccessRate | Logs Service Worker | Quotidien | Évaluer l’efficacité du préchargement |
Le préchargement reste circonstanciel ; basez-vous sur ces signaux pour décider si un budget existe à l’instant T.
// sw/budget.ts
export function calculateBudget({ downlink, lcpSize, concurrentLoads }: {
downlink: number
lcpSize: number
concurrentLoads: number
}) {
const capacity = downlink * 125000 // Mbps -> bytes/s
const reserved = lcpSize + concurrentLoads * 150000
return Math.max(0, capacity * 0.25 - reserved)
}
Construire la file de préchargement
Les candidats sont décrits dans prefetch-manifest.json
.
[
{
"id": "hero-day2",
"url": "/images/event/day2@2x.avif",
"priority": 0.9,
"type": "image",
"expectedSize": 320000
},
{
"id": "gallery-mini",
"url": "/images/gallery/thumbs.webp",
"priority": 0.4,
"type": "image",
"expectedSize": 90000
}
]
Le Service Worker lit le manifeste et ne met en file que ce qui tient dans le budget courant.
// sw/prefetch.ts
import { calculateBudget } from './budget'
import manifest from '../prefetch-manifest.json'
self.addEventListener('message', event => {
if (event.data?.type !== 'INIT_PREFETCH') return
const state = event.data.state
const budget = calculateBudget({
downlink: state.downlink,
lcpSize: state.lcpSize,
concurrentLoads: state.concurrentLoads
})
const queue = manifest
.filter(item => item.expectedSize <= budget)
.sort((a, b) => b.priority - a.priority)
prefetchQueue(queue)
})
async function prefetchQueue(queue) {
for (const entry of queue) {
const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), 4000)
try {
await fetch(entry.url, {
priority: entry.priority > 0.7 ? 'high' : 'low',
signal: controller.signal
})
await caches.open('prefetch-v1').then(cache => cache.add(entry.url))
logPrefetch(entry.id, true)
} catch (error) {
logPrefetch(entry.id, false, error)
} finally {
clearTimeout(timeout)
}
}
}
fetchpriority
reste expérimental mais fonctionne déjà dans Chrome/Safari. Pour les navigateurs sans support de l’option priority
, implémentez un repli qui réécrit l’attribut <link fetchpriority>
.
Orchestrer Priority Hints et HTML
// app/layout.tsx
export function PrefetchHints() {
return (
<>
<link
rel="preload"
as="image"
href="/images/event/day2@2x.avif"
fetchPriority="low"
/>
<script
dangerouslySetInnerHTML={{
__html: `navigator.serviceWorker?.controller?.postMessage({
type: 'INIT_PREFETCH',
state: {
downlink: navigator.connection?.downlink || 1.5,
lcpSize: window.__LCP_SIZE__ || 200000,
concurrentLoads: window.__IN_FLIGHT__ || 0
}
});`
}}
/>
</>
)
}
Stratégie d’annulation pour protéger l’INP
Dès que l’INP augmente, stoppez le préchargement et diminuez la priorité pour les visites suivantes.
// sw/inp-monitor.ts
const INP_THRESHOLD = 200
new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
if (entry.duration > INP_THRESHOLD) {
self.registration.active?.postMessage({ type: 'CANCEL_PREFETCH' })
updatePriority(entry.eventType)
}
}
}).observe({ type: 'event', buffered: true })
CANCEL_PREFETCH
interrompt la file courante ; décrémenter priority
de 0,1 permet aux pages riches en interactions de restreindre d’elles-mêmes le préchargement.
Background Sync et préchargement nocturne
Forcer le préchargement sur une connexion faible bloque l’interface. Utilisez periodicSync
pour réessayer en Wi-Fi ou en heures creuses.
// sw/background-sync.ts
self.addEventListener('sync', event => {
if (event.tag !== 'prefetch-sync') return
event.waitUntil(prefetchQueue(manifest))
})
async function scheduleSync() {
const registration = await self.registration.periodicSync?.register('prefetch-sync', {
minInterval: 6 * 60 * 60 * 1000
})
return registration
}
Monitoring et analytics
Envoyez des événements RUM vers performance-guardian pour mesurer l’apport du préchargement.
sendToAnalytics('prefetch', {
budget,
downlink,
prefetchSuccessRate,
deltaLCP,
deltaINP
})
Tableau de bord recommandé :
KPI | Cible | Condition d’alerte |
---|---|---|
ΔLCP (avec vs sans préchargement) | ≈ -180 ms | Valeurs positives 3 jours d’affilée |
INP p75 | < 180 ms | > 200 ms → arrêt immédiat |
Taux de succès du préfetch | > 85 % | < 70 % → revoir le manifeste |
Occupation de bande passante | < 30 % | > 50 % → suspendre l’expérience |
Checklist
- [ ]
prefetch-manifest.json
est soumis à revue de code. - [ ] Les paramètres de
calculateBudget
sont validés via tests A/B. - [ ] Le préfetch s’arrête instantanément quand l’INP se dégrade.
- [ ] Background Sync ne relance qu’en Wi-Fi.
- [ ] performance-guardian suit ΔLCP / ΔINP.
- [ ] Le taux de hit CDN est comparé avant/après déploiement.
En résumé
Un préchargement non budgété peut consommer la bande passante et détériorer l’INP. En combinant modèle de budget, priorisation dynamique et Background Sync, vous conditionnez le préfetch et améliorez le LCP tout en préservant l’expérience. Associez APIs navigateur et automatisation CI pour bâtir des boucles de contrôle, puis faites évoluer la stratégie selon chaque site.
Outils associés
Gardien des performances
Modélise les budgets de latence, suit les dépassements de SLO et exporte des preuves pour les revues d'incident.
Compare Slider
Intuitive before/after comparison.
Budgets de qualité d'image & portes CI
Définir des budgets ΔE2000/SSIM/LPIPS, simuler des portes CI et exporter des garde-fous.
Journal d'audit
Consigner les actions de remédiation sur les couches image, métadonnées et utilisateur avec des traces d'audit exportables.
Articles liés
Modération de streaming sensible aux pertes 2025 — Piloter la bande passante AVIF/HEIC avec des SLO de qualité
Guide terrain pour équilibrer limitation de bande passante et SLO de qualité lors de la diffusion de formats très compressés comme AVIF/HEIC. Couvre les schémas de contrôle, la supervision et la stratégie de rollback.
Automatiser l’optimisation d’images avec un pipeline WASM 2025 — Intégrer esbuild et Lightning CSS
Modèles pour automatiser la génération, la validation et la signature de dérivés d’images avec une chaîne de compilation compatible WASM. Montre comment combiner esbuild, Lightning CSS et Squoosh CLI pour obtenir un CI/CD reproductible.
Stratégie complète de compression d'images 2025 — Guide pratique pour optimiser la vitesse perçue tout en préservant la qualité
Stratégies de compression d'images de pointe efficaces pour Core Web Vitals et l'exploitation réelle, avec des presets spécifiques par usage, du code et des flux de travail expliqués en détail. Couvre la distinction JPEG/PNG/WebP/AVIF, l'optimisation build/livraison et le diagnostic de dépannage.
Auditeur de niveaux de service CDN 2025 — Surveiller les SLA d’image avec des preuves concrètes
Architecture d’audit pour démontrer le respect des SLA d’image dans un contexte multi-CDN. Inclut stratégie de mesure, collecte d’éléments probants et reporting prêt pour la négociation.
Monitoring pratique des Core Web Vitals 2025 — Checklist SRE pour projets enterprise
Un playbook orienté SRE qui aide les équipes de production web enterprise à industrialiser les Core Web Vitals, du design SLO à la collecte de données et à la réponse aux incidents.
Observabilité de la diffusion d’images Edge 2025 — Guide SLO et opérations pour agences web
Détaille la conception des SLO, les tableaux de bord de mesure et l’exploitation des alertes pour suivre la qualité de diffusion des images via les CDN Edge et navigateurs, avec exemples Next.js et GraphQL adaptés aux agences web.