TL;DR: Lighthouse mide Core Web Vitals (LCP, INP, CLS) y da scores de Performance, Accessibility, Best Practices y SEO. Ejecútalo en Chrome DevTools, vía CLI o automatiza con Lighthouse CI en tu pipeline.
Google Lighthouse es la herramienta de auditoría de rendimiento web más utilizada, integrada directamente en Chrome DevTools y usada por millones de desarrolladores. Según el HTTP Archive Web Almanac 2024, solo el 43% de las páginas móviles alcanzan el estado “Bueno” en los tres Core Web Vitals — LCP, INP y CLS. Google confirmó en 2021 que los Core Web Vitals son factores de ranking, lo que vincula directamente los scores de Lighthouse con la visibilidad en búsquedas. Lighthouse proporciona scores compuestos para Performance, Accessibility, Best Practices y SEO, con cada score derivado de combinaciones de métricas ponderadas. Esta guía cubre Lighthouse desde la primera auditoría hasta la integración de presupuestos de rendimiento en pipelines CI/CD.
¿Qué es Lighthouse?
Lighthouse es una herramienta automatizada de código abierto de Google para mejorar la calidad de páginas web. Proporciona auditorías integrales de rendimiento, accesibilidad, progressive web apps, SEO y mejores prácticas. Para ingenieros QA, Lighthouse es esencial para validar que las aplicaciones cumplan estándares modernos de rendimiento web, especialmente Core Web Vitals — las métricas clave de Google para experiencia de usuario.
Por qué Lighthouse Importa para QA
- Core Web Vitals Impactan SEO - Google usa LCP, FID y CLS como factores de ranking
- Validación de Experiencia de Usuario - Cuantifica el rendimiento percibido con métricas reales
- Integración CI/CD - Automatiza pruebas de regresión de rendimiento en pipelines
- Insights Accionables - Obtén recomendaciones específicas para mejoras de rendimiento
- Rendimiento Móvil - Prueba diseño responsive y problemas específicos de móvil
Las pruebas de rendimiento con Lighthouse se complementan perfectamente con pruebas de rendimiento de APIs para validar tiempos de respuesta del backend. Integrar Lighthouse en tu pipeline CI/CD como parte de una estrategia de testing continuo asegura que las regresiones de rendimiento se detecten automáticamente antes de llegar a producción.
Core Web Vitals Explicados
Core Web Vitals son tres métricas clave que miden la experiencia de usuario en el mundo real:
1. Largest Contentful Paint (LCP)
Qué mide: Rendimiento de carga - cuando el elemento de contenido más grande se vuelve visible.
Umbrales:
- Bueno: ≤ 2.5 segundos
- Necesita Mejora: 2.5 - 4.0 segundos
- Pobre: > 4.0 segundos
Problemas Comunes:
// Problema: JavaScript bloqueante de renderizado
<script src="analytics.js"></script>
// Solución: Diferir scripts no críticos
<script src="analytics.js" defer></script>
// Problema: Imágenes no optimizadas
<img src="hero-10mb.jpg" alt="Hero">
// Solución: Formatos modernos + imágenes responsive
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero" loading="lazy">
</picture>
Estrategias de Optimización:
- Usar CDN para assets estáticos
- Implementar lazy loading para imágenes below-fold
- Precargar recursos críticos:
<link rel="preload" href="hero.jpg" as="image"> - Optimizar tiempos de respuesta del servidor (TTFB < 600ms)
- Usar resource hints:
dns-prefetch,preconnect
2. First Input Delay (FID) / Interaction to Next Paint (INP)
Qué mide: Interactividad - retraso entre interacción del usuario y respuesta del navegador.
Umbrales FID (siendo reemplazado por INP):
- Bueno: ≤ 100ms
- Necesita Mejora: 100 - 300ms
- Pobre: > 300ms
Umbrales INP (nueva métrica desde 2024):
- Bueno: ≤ 200ms
- Necesita Mejora: 200 - 500ms
- Pobre: > 500ms
Problemas Comunes:
// Problema: Tareas largas de JavaScript bloquean el hilo principal
function processHugeDataset() {
// Loop síncrono procesando 100k items
for (let i = 0; i < 100000; i++) {
heavyCalculation(data[i]);
}
}
// Solución: Dividir en chunks pequeños con setTimeout
async function processHugeDataset() {
const chunkSize = 1000;
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
chunk.forEach(item => heavyCalculation(item));
// Ceder al navegador cada chunk
await new Promise(resolve => setTimeout(resolve, 0));
}
}
// Solución: Usar Web Workers para cálculos pesados
// main.js
const worker = new Worker('calculator.worker.js');
worker.postMessage({ data: largeDataset });
worker.onmessage = (e) => {
displayResults(e.data);
};
// calculator.worker.js
self.onmessage = (e) => {
const results = heavyCalculation(e.data);
self.postMessage(results);
};
Estrategias de Optimización:
- Dividir tareas largas en chunks pequeños (< 50ms cada uno)
- Usar code splitting para reducir tamaño de bundle JavaScript
- Diferir scripts de terceros
- Implementar service workers para mejor caching
- Usar
requestIdleCallbackpara trabajo no urgente
3. Cumulative Layout Shift (CLS)
Qué mide: Estabilidad visual - cambios inesperados de layout durante carga de página.
Umbrales:
- Bueno: ≤ 0.1
- Necesita Mejora: 0.1 - 0.25
- Pobre: > 0.25
Problemas Comunes:
<!-- Problema: Imágenes sin dimensiones -->
<img src="product.jpg" alt="Product">
<!-- Solución: Siempre especificar width/height -->
<img src="product.jpg" alt="Product" width="800" height="600">
<!-- Problema: Inserción dinámica de contenido -->
<div id="banner"></div>
<script>
// Carga ad asíncronamente, empujando contenido hacia abajo
loadAdvertisement('#banner');
</script>
<!-- Solución: Reservar espacio para contenido dinámico -->
<div id="banner" style="min-height: 250px;">
<!-- Ad se carga aquí sin layout shift -->
</div>
/* Problema: Web fonts causan layout shift (FOIT/FOUT) */
@font-face {
font-family: 'CustomFont';
src: url('custom-font.woff2') format('woff2');
}
/* Solución: Usar font-display para controlar comportamiento de carga */
@font-face {
font-family: 'CustomFont';
src: url('custom-font.woff2') format('woff2');
font-display: swap; /* Mostrar fallback inmediatamente, intercambiar cuando cargue */
}
/* Mejor: Precargar fuentes críticas */
/* En HTML: <link rel="preload" href="custom-font.woff2" as="font" crossorigin> */
Estrategias de Optimización:
- Siempre incluir atributos de tamaño en imágenes y videos
- Reservar espacio para ads y embeds con
min-height - Evitar insertar contenido arriba de contenido existente
- Usar animaciones
transformen lugar de propiedades que disparan layout - Precargar fuentes personalizadas y usar
font-display: swap
Ejecutar Pruebas Lighthouse
1. Chrome DevTools (Pruebas Manuales)
# Abrir Chrome DevTools
# 1. Presionar F12 o Cmd+Option+I
# 2. Ir a pestaña "Lighthouse"
# 3. Seleccionar categorías (Performance, Accessibility, etc.)
# 4. Elegir dispositivo (Mobile/Desktop)
# 5. Hacer clic en "Analyze page load"
Mejores Prácticas para Pruebas Manuales:
- Probar en modo Incógnito (sin interferencia de extensiones)
- Usar throttling para simular condiciones del mundo real (Slow 4G, 4x CPU slowdown)
- Ejecutar múltiples pruebas y promediar puntuaciones (resultados pueden variar ±5 puntos)
- Probar configuraciones móvil y desktop
2. Lighthouse CLI (Pruebas Automatizadas)
# Instalar Lighthouse
npm install -g lighthouse
# Prueba básica
lighthouse https://example.com
# Prueba móvil con formatos de salida
lighthouse https://example.com \
--output html,json,csv \
--output-path ./reports/report \
--view \
--chrome-flags="--headless"
# Prueba con throttling específico
lighthouse https://example.com \
--throttling.rttMs=150 \
--throttling.throughputKbps=1600 \
--throttling.cpuSlowdownMultiplier=4
# Probar solo categorías específicas
lighthouse https://example.com \
--only-categories=performance,accessibility
# Archivo de config personalizado
lighthouse https://example.com \
--config-path=./lighthouse-config.js
3. Lighthouse CI (Integración Continua)
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on:
pull_request:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Start server
run: npm start &
- name: Wait for server
run: npx wait-on http://localhost:3000
- name: Run Lighthouse CI
run: |
npm install -g @lhci/cli
lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
- name: Upload results
uses: actions/upload-artifact@v3
with:
name: lighthouse-reports
path: .lighthouseci/
Configuración lighthouserc.json:
{
"ci": {
"collect": {
"url": ["http://localhost:3000"],
"numberOfRuns": 3,
"settings": {
"preset": "desktop",
"throttling": {
"rttMs": 40,
"throughputKbps": 10240,
"cpuSlowdownMultiplier": 1
}
}
},
"assert": {
"preset": "lighthouse:recommended",
"assertions": {
"categories:performance": ["error", {"minScore": 0.9}],
"categories:accessibility": ["error", {"minScore": 0.9}],
"first-contentful-paint": ["error", {"maxNumericValue": 2000}],
"largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
"cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
"total-blocking-time": ["error", {"maxNumericValue": 300}]
}
},
"upload": {
"target": "temporary-public-storage"
}
}
}
Interpretar Puntuaciones Lighthouse
Desglose de Puntuación de Rendimiento
La puntuación de Performance (0-100) se pondera entre múltiples métricas:
| Métrica | Peso | Descripción |
|---|---|---|
| First Contentful Paint (FCP) | 10% | Primer texto/imagen renderizado |
| Largest Contentful Paint (LCP) | 25% | Elemento de contenido más grande visible |
| Total Blocking Time (TBT) | 30% | Tiempo total que el hilo principal está bloqueado |
| Cumulative Layout Shift (CLS) | 25% | Puntuación de estabilidad visual |
| Speed Index | 10% | Qué tan rápido se muestra visualmente el contenido |
Rangos de Puntuación:
- 90-100 (Verde): Bueno - cumple mejores prácticas de rendimiento
- 50-89 (Naranja): Necesita mejora - algo de optimización requerida
- 0-49 (Rojo): Pobre - problemas significativos de rendimiento
Estrategia de Presupuesto de Rendimiento
// lighthouserc-budget.json
{
"ci": {
"assert": {
"assertions": {
// Umbrales Core Web Vitals
"largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
"cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
"total-blocking-time": ["error", {"maxNumericValue": 300}],
// Presupuestos de recursos
"resource-summary:script:size": ["error", {"maxNumericValue": 300000}],
"resource-summary:image:size": ["error", {"maxNumericValue": 500000}],
"resource-summary:stylesheet:size": ["error", {"maxNumericValue": 50000}],
"resource-summary:document:size": ["error", {"maxNumericValue": 50000}],
"resource-summary:font:size": ["error", {"maxNumericValue": 100000}],
"resource-summary:third-party:size": ["error", {"maxNumericValue": 200000}],
// Presupuestos de cantidad de solicitudes
"resource-summary:script:count": ["error", {"maxNumericValue": 15}],
"resource-summary:third-party:count": ["error", {"maxNumericValue": 10}]
}
}
}
}
Técnicas Avanzadas de Lighthouse
1. Auditorías Personalizadas
// custom-audit.js
class CustomPerformanceAudit {
static get meta() {
return {
id: 'custom-api-performance',
title: 'Tiempo de Respuesta API',
failureTitle: 'Respuestas API son lentas',
description: 'Mide tiempos de respuesta de endpoints API',
requiredArtifacts: ['devtoolsLogs']
};
}
static async audit(artifacts) {
const devtools = artifacts.devtoolsLogs.defaultPass;
const networkRecords = /* extraer registros de red */;
const apiCalls = networkRecords.filter(r =>
r.url.includes('/api/') && r.finished
);
const avgResponseTime = apiCalls.reduce((sum, call) =>
sum + call.endTime - call.startTime, 0
) / apiCalls.length;
const passed = avgResponseTime < 500; // umbral 500ms
return {
score: passed ? 1 : 0,
numericValue: avgResponseTime,
displayValue: `${Math.round(avgResponseTime)}ms promedio`,
details: {
type: 'table',
headings: [
{key: 'url', text: 'URL'},
{key: 'responseTime', text: 'Tiempo Respuesta'}
],
items: apiCalls.map(call => ({
url: call.url,
responseTime: `${Math.round(call.endTime - call.startTime)}ms`
}))
}
};
}
}
module.exports = CustomPerformanceAudit;
2. Pruebas de Flujo de Usuario (Nuevo en Lighthouse 9+)
// user-flow-test.js
import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js';
import puppeteer from 'puppeteer';
async function runUserFlow() {
const browser = await puppeteer.launch({headless: 'new'});
const page = await browser.newPage();
const flow = await startFlow(page, {name: 'Flujo checkout e-commerce'});
// Paso 1: Navegar a homepage
await flow.navigate('https://example.com', {
stepName: 'Homepage'
});
// Paso 2: Buscar producto
await flow.startTimespan({stepName: 'Interacción búsqueda'});
await page.type('#search', 'laptop');
await page.click('#search-button');
await page.waitForSelector('.search-results');
await flow.endTimespan();
// Paso 3: Página detalles producto
await flow.navigate('https://example.com/product/123', {
stepName: 'Página producto'
});
// Paso 4: Interacción añadir al carrito
await flow.startTimespan({stepName: 'Añadir al carrito'});
await page.click('#add-to-cart');
await page.waitForSelector('.cart-notification');
await flow.endTimespan();
// Paso 5: Snapshot checkout
await flow.snapshot({stepName: 'Estado página carrito'});
// Generar reporte
const report = await flow.generateReport();
fs.writeFileSync('user-flow-report.html', report);
await browser.close();
}
runUserFlow();
3. Dashboard de Monitoreo de Rendimiento
# lighthouse-monitor.py
import subprocess
import json
import datetime
from influxdb import InfluxDBClient
def run_lighthouse(url):
"""Ejecutar Lighthouse y retornar métricas"""
result = subprocess.run([
'lighthouse',
url,
'--output=json',
'--output-path=stdout',
'--chrome-flags="--headless"'
], capture_output=True, text=True)
return json.loads(result.stdout)
def extract_metrics(report):
"""Extraer métricas clave del reporte Lighthouse"""
audits = report['audits']
return {
'performance_score': report['categories']['performance']['score'] * 100,
'fcp': audits['first-contentful-paint']['numericValue'],
'lcp': audits['largest-contentful-paint']['numericValue'],
'tbt': audits['total-blocking-time']['numericValue'],
'cls': audits['cumulative-layout-shift']['numericValue'],
'speed_index': audits['speed-index']['numericValue'],
}
def send_to_influxdb(metrics, url):
"""Enviar métricas a InfluxDB para visualización Grafana"""
client = InfluxDBClient(host='localhost', port=8086, database='lighthouse')
point = {
'measurement': 'performance',
'tags': {
'url': url,
'device': 'mobile'
},
'time': datetime.datetime.utcnow().isoformat(),
'fields': metrics
}
client.write_points([point])
# Monitorear páginas cada hora
urls = [
'https://example.com',
'https://example.com/products',
'https://example.com/checkout'
]
for url in urls:
report = run_lighthouse(url)
metrics = extract_metrics(report)
send_to_influxdb(metrics, url)
print(f"✓ {url}: Puntuación Rendimiento {metrics['performance_score']}")
Cuellos de Botella Comunes y Correcciones
Problema: Bundles JavaScript Grandes
# Analizar tamaño de bundle
npm install -g webpack-bundle-analyzer
# Añadir a webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
};
# Build y analizar
npm run build
Soluciones:
- Code splitting:
import('./heavy-component.js').then(module => ...) - Tree shaking: Asegurar
"sideEffects": falseen package.json - Eliminar dependencias no usadas
- Usar imports dinámicos para code splitting basado en rutas
Problema: Recursos Bloqueantes de Renderizado
<!-- Problema: CSS bloqueante -->
<link rel="stylesheet" href="styles.css">
<!-- Solución: CSS crítico inline, diferir no crítico -->
<style>
/* CSS crítico above-fold inline */
.header { /* ... */ }
.hero { /* ... */ }
</style>
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
Problema: Imágenes No Optimizadas
# Instalar herramientas de optimización de imágenes
npm install -D imagemin imagemin-webp imagemin-avif
# optimize-images.js
const imagemin = require('imagemin');
const imageminWebp = require('imagemin-webp');
const imageminAvif = require('imagemin-avif');
(async () => {
await imagemin(['images/*.{jpg,png}'], {
destination: 'images/optimized',
plugins: [
imageminWebp({quality: 80}),
imageminAvif({quality: 65})
]
});
})();
Conclusión
Lighthouse es una herramienta indispensable para pruebas modernas de rendimiento web. Al integrar Lighthouse en tu flujo de trabajo QA — desde auditorías manuales DevTools hasta pipelines CI/CD (como se discute en Load Testing with JMeter: Complete Guide) automatizados — aseguras que las aplicaciones cumplan estándares Core Web Vitals y entreguen experiencias excepcionales de usuario.
Conclusiones Clave:
- Enfócate en Core Web Vitals: LCP, INP/FID, CLS
- Automatiza pruebas Lighthouse en pipelines CI/CD
- (como se discute en Burp Suite for QA Engineers: Complete Security Testing Guide) Establece presupuestos de rendimiento y aplícalos
- Usa pruebas User Flow para interacciones complejas
- Monitorea rendimiento en el tiempo con dashboards
- Optimiza imágenes, JavaScript y rutas de renderizado
El rendimiento es una característica, no una reflexión tardía. Haz de las pruebas Lighthouse una parte central de tu proceso QA.
Documentacion Relacionada
- Pruebas de Rendimiento de APIs - Complementa Lighthouse con pruebas de rendimiento del backend
- Optimización de Pipelines CI/CD para Equipos QA - Integra Lighthouse en tu pipeline de entrega continua
- Testing Continuo en DevOps - Automatiza auditorías de rendimiento en cada despliegue
- Guía Completa de Playwright - Combina Lighthouse con Playwright para pruebas E2E con métricas de rendimiento
- Estrategia de Automatización de Pruebas - Incluye pruebas de rendimiento en tu estrategia de automatización
Recursos Oficiales
“Los scores de Lighthouse son síntomas, no la enfermedad. Un score de 60 en Performance dice que algo está mal. El waterfall chart y la métrica específica que falla te dicen qué corregir. Siempre empieza con el Core Web Vital que falla, no con el score compuesto.” — Yuri Kan, Senior QA Lead
FAQ
¿Qué mide Lighthouse?
Lighthouse mide Performance (LCP, INP, CLS, FCP, TTFB), Accessibility, Best Practices y SEO con scores ponderados para cada uno.
El score de Performance de Lighthouse pondera seis métricas: Largest Contentful Paint (25%), Total Blocking Time (30%), Cumulative Layout Shift (25%), First Contentful Paint (10%), Speed Index (10%). Accessibility verifica 70+ criterios incluyendo ratios de contraste, etiquetas ARIA y navegación por teclado.
¿Qué es un buen score de Lighthouse?
Google define 90-100 como Bueno, 50-89 como Necesita Mejora, 0-49 como Pobre. Los umbrales de Core Web Vitals importan más que el score compuesto.
Apunta a 90+ en el score de Performance, pero prioriza los umbrales de Core Web Vitals: LCP bajo 2.5s, INP bajo 200ms, CLS bajo 0.1. Una página con score 85 con todos los Core Web Vitals en rango “Bueno” tendrá mejor ranking que una con score 92 pero con un Core Web Vital en “Necesita Mejora”.
¿Cómo ejecutar Lighthouse en CI/CD?
Instala Lighthouse CI (npm install -g @lhci/cli), configura lhci collect apuntando a tu URL de staging y usa lhci assert para establecer umbrales de scores.
Lighthouse CI (LHCI) automatiza las ejecuciones y comparaciones de Lighthouse. Configura un lighthouserc.json con tu URL, número de ejecuciones (3+ para precisión) y umbrales de assertion. La integración con GitHub Actions via treosh/lighthouse-ci-action sube reportes y comenta resultados en PRs.
¿Qué son los Core Web Vitals?
LCP (carga), INP (interactividad) y CLS (estabilidad visual) — las tres métricas de experiencia de usuario de Google y factores de ranking de búsqueda.
Largest Contentful Paint (LCP) mide cuánto tarda en cargarse el contenido principal — umbral: bajo 2.5s. Interaction to Next Paint (INP) mide la capacidad de respuesta a las interacciones del usuario — umbral: bajo 200ms. Cumulative Layout Shift (CLS) mide la estabilidad visual durante la carga — umbral: bajo 0.1.
