Debugging con DevTools
Long Animation Frames API funciona en conjunto con DevTools, no es un reemplazo. Esta demo muestra técnicas prácticas para usar LoAF mientras debuggeas en Chrome DevTools.
Hemos creado un WebPerf Snippet listo para copiar y pegar en DevTools. → Ver LoAF Helpers Snippet
Abre DevTools (F12) y ve a la pestaña Console mientras usas esta
página. Los helpers están disponibles globalmente en window.loafHelpers.
Simular actividad
loafHelpers.summary()- Resumen de todos los framesloafHelpers.topScripts(n)- Top N scripts más lentosloafHelpers.exportCSV()- Descargar datos como CSVloafHelpers.exportJSON()- Descargar datos como JSONloafHelpers.clear()- Limpiar datos capturados
Helpers de debugging
Estos helpers están diseñados para usarse en la consola de DevTools durante el debugging.
Se instalan automáticamente en window.loafHelpers.
1. Summary - Resumen rápido
Muestra un resumen de todos los frames capturados.
loafHelpers.summary()
Salida: Total frames, tiempo total, avg, max, distribución por severidad
2. Top Scripts - Scripts más problemáticos
Lista los N scripts con mayor impacto.
loafHelpers.topScripts(5) // Top 5 scripts
Salida: Tabla con sourceURL, función, duración, forzado layout
3. Filter by Duration - Filtrar por duración
Mostrar solo frames que superen cierto umbral.
loafHelpers.filter({ minDuration: 200 })
4. Find by URL - Buscar por script
Filtrar frames que incluyan un script específico.
loafHelpers.findByURL('analytics')
5. Export Data - Exportar datos
Descargar todos los datos capturados para análisis externo.
loafHelpers.exportJSON() // Descarga JSON
loafHelpers.exportCSV() // Descarga CSV
Workflow recomendado
1. Durante desarrollo
// En Console de DevTools:
// 1. Usar la página normalmente
// 2. Ver resumen
loafHelpers.summary()
// 3. Identificar scripts problemáticos
loafHelpers.topScripts(10)
// 4. Filtrar frames críticos
loafHelpers.filter({ minDuration: 200 })
// 5. Buscar un script específico
loafHelpers.findByURL('my-component')
2. Para análisis profundo
// 1. Exportar datos
loafHelpers.exportJSON()
// 2. Analizar en herramienta externa
// - Excel/Google Sheets
// - Script de Python/Node.js
// - BI tools (Tableau, Metabase, etc.)
// 3. O acceder a datos raw
const data = loafHelpers.getRawData()
console.table(data)
3. Performance recording
- Abre DevTools → Performance
- Click "Record" 🔴
- Reproduce el problema
- Stop recording
- En Console:
loafHelpers.summary()para correlacionar
Los timestamps de LoAF coinciden con el timeline de Performance, puedes correlacionar frames lentos con eventos específicos.
Console API útiles
console.table() para LoAF
// Mostrar scripts en tabla ordenada
const frames = performance.getEntriesByType('long-animation-frame');
const scripts = frames.flatMap(f => f.scripts).map(s => ({
url: new URL(s.sourceURL || location.href).pathname,
function: s.sourceFunctionName || '(anónima)',
duration: s.duration.toFixed(2) + 'ms',
forced: s.forcedStyleAndLayoutDuration.toFixed(2) + 'ms'
}));
console.table(scripts);
console.group() para organización
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.group(`Frame lento: ${entry.duration.toFixed(2)}ms`);
console.log('Duración total:', entry.duration);
console.log('Render start:', entry.renderStart);
console.log('Scripts:', entry.scripts.length);
console.groupCollapsed('Detalle de scripts');
entry.scripts.forEach(s => {
console.log(s.sourceURL, s.duration);
});
console.groupEnd();
console.groupEnd();
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
console.time() para mediciones
// Medir cuánto tarda en detectarse un frame lento
button.addEventListener('click', () => {
console.time('hasta-loaf');
// Operación pesada
heavyWork();
});
const observer = new PerformanceObserver((list) => {
console.timeEnd('hasta-loaf');
});
observer.observe({ type: 'long-animation-frame', buffered: true });
DevTools Performance panel
LoAF API captura información que complementa el Performance panel, no lo reemplaza. Usa ambos en conjunto:
- Performance panel: Flamegraph detallado, waterfall, screenshots
- LoAF API: Attribution automática, análisis en producción, métricas agregadas
Correlacionar LoAF con timeline
// Capturar timestamp de frames lentos
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(
`Frame lento en ${entry.startTime.toFixed(2)}ms` +
` (duración: ${entry.duration.toFixed(2)}ms)`
);
// Buscar en timeline de Performance panel alrededor de startTime
// para ver el flamegraph detallado
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Performance marks
// Marcar eventos específicos para correlacionar
function heavyOperation() {
performance.mark('heavy-operation-start');
// ... trabajo pesado
performance.mark('heavy-operation-end');
performance.measure(
'heavy-operation',
'heavy-operation-start',
'heavy-operation-end'
);
}
// En DevTools Performance, verás las marks en el timeline
// Y en LoAF verás si causaron frames lentos
Snippets útiles
Snippet: Auto-log de frames críticos
// Guardar como Snippet en DevTools → Sources → Snippets
(function() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 200) {
console.group(`🚨 Frame crítico: ${entry.duration.toFixed(2)}ms`);
console.log('Timestamp:', entry.startTime);
console.log('Scripts involucrados:', entry.scripts.length);
const topScript = entry.scripts.reduce((max, s) =>
s.duration > max.duration ? s : max
);
console.warn('Script más lento:', {
url: topScript.sourceURL,
function: topScript.sourceFunctionName,
duration: topScript.duration
});
console.groupEnd();
}
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
console.log('✅ Monitoring de frames críticos activado');
})();
Snippet: Comparador de sesiones
// Capturar baseline y comparar después
window.loafBaseline = {
capture() {
const frames = performance.getEntriesByType('long-animation-frame');
this.data = {
count: frames.length,
totalTime: frames.reduce((sum, f) => sum + f.duration, 0),
avgDuration: frames.reduce((sum, f) => sum + f.duration, 0) / frames.length,
timestamp: Date.now()
};
console.log('📊 Baseline capturado:', this.data);
},
compare() {
const frames = performance.getEntriesByType('long-animation-frame');
const current = {
count: frames.length,
totalTime: frames.reduce((sum, f) => sum + f.duration, 0),
avgDuration: frames.reduce((sum, f) => sum + f.duration, 0) / frames.length
};
console.group('📈 Comparación vs Baseline');
console.log('Frames:', current.count, 'vs', this.data.count,
`(${((current.count / this.data.count - 1) * 100).toFixed(1)}%)`);
console.log('Avg duration:', current.avgDuration.toFixed(2) + 'ms', 'vs',
this.data.avgDuration.toFixed(2) + 'ms',
`(${((current.avgDuration / this.data.avgDuration - 1) * 100).toFixed(1)}%)`);
console.groupEnd();
}
};
// Uso:
// loafBaseline.capture() // Antes de cambio
// ... hacer cambios ...
// loafBaseline.compare() // Después de cambio
💾 Exportar datos de esta sesión
Descarga los datos capturados para análisis offline: