LCP y el main thread
LCP (Largest Contentful Paint) mide cuándo se pinta el elemento más grande visible en el viewport. Es una métrica crítica de Core Web Vitals.
JavaScript que se ejecuta en el main thread puede bloquear o retrasar el LCP. Long Animation Frames API nos permite identificar exactamente qué scripts son los culpables.
Carga real con scripts bloqueantes
Timeline de eventos:
Haz clic en uno de los botones para cargar la página
Análisis detallado:
¿Cómo los frames lentos afectan LCP?
1. Bloqueo del parser HTML
JavaScript síncrono bloquea el parser, retrasando el descubrimiento y renderizado del elemento LCP.
<!-- HTML -->
<script src="blocking.js"></script> ← Bloquea
<img src="hero.jpg" /> ← LCP element (retrasado)
2. Bloqueo del render
Scripts que se ejecutan justo antes o durante el paint del LCP pueden retrasarlo.
3. Layout thrashing
Recálculos de layout fuerzan al navegador a retrasar el paint del LCP.
Detectar frames que bloquean LCP
1. Capturar el LCP
const lcpObserver = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
console.log('LCP:', lastEntry.renderTime || lastEntry.loadTime);
});
lcpObserver.observe({
type: 'largest-contentful-paint',
buffered: true
});
2. Buscar LoAFs que ocurrieron antes del LCP
function findLcpBlockingFrames(lcpTime) {
const loafs = performance.getEntriesByType('long-animation-frame');
return loafs.filter(loaf => {
// ¿El LoAF ocurrió antes del LCP?
return loaf.startTime < lcpTime;
});
}
3. Analizar el impacto
const blockingFrames = findLcpBlockingFrames(lcpTime);
blockingFrames.forEach(loaf => {
console.log('Frame que bloqueó LCP:');
console.log(' Duración:', loaf.duration);
console.log(' Blocking:', loaf.blockingDuration);
loaf.scripts.forEach(script => {
console.log(' Script:', script.sourceFunctionName);
console.log(' Tiempo:', script.duration);
});
});
Umbrales de LCP
| Rating | Tiempo | Interpretación |
|---|---|---|
| 🟢 Bueno | < 2.5s | Experiencia rápida |
| 🟡 Necesita mejora | 2.5s - 4s | Mejorable |
| 🔴 Malo | > 4s | Experiencia lenta |
Estrategias de optimización
Si encuentras frames bloqueando LCP:
<script src="analytics.js" defer></script>
<script src="ads.js" async></script>
Cargar solo el código necesario para el LCP, diferir el resto
El HTML ya incluye el contenido del LCP, menos JavaScript para bloquear
- Scripts síncronos grandes antes del LCP element
- Layout thrashing durante la carga inicial
- Fuentes web que bloquean el render (usar font-display: swap)