Por qué el rendimiento es una funcionalidad
El rendimiento no es un paso de pulido al final de un proyecto. Es lo que decide si los usuarios llegan a ver tu contenido o no. Una página que tarda cuatro segundos en mostrar su contenido principal pierde una gran parte de sus visitantes antes siquiera de ser usable, y las interacciones lentas hacen que una aplicación se sienta rota incluso cuando funciona correctamente.
La buena noticia es que el rendimiento es medible y, en su mayoría, sistemático. Un pequeño número de palancas —cargar menos, cargar más tarde, realizar menos trabajo en el hilo principal— representan la gran mayoría de las mejoras. Esta guía cubre las métricas, las herramientas y las técnicas que más importan.
Core Web Vitals
Tres métricas, denominadas colectivamente Core Web Vitals, describen la experiencia del usuario:
- Largest Contentful Paint (LCP) — indica cuándo termina de renderizarse el elemento visible más grande. Un buen resultado es inferior a 2.5 segundos.
- Interaction to Next Paint (INP) — la latencia entre una interacción del usuario y la siguiente actualización visual. Un buen resultado es inferior a 200 milisegundos.
- Cumulative Layout Shift (CLS) — cuánto se desplaza el contenido visible de forma inesperada. Un buen resultado es inferior a 0.1.
Otras métricas complementarias incluyen Time to First Byte (TTFB) para la latencia del servidor y First Contentful Paint (FCP) para el momento en que aparece el primer elemento. En conjunto, estas métricas te indican si una página es rápida, responsiva y estable.
Medición
Necesitas tanto datos de campo (field data) como datos de laboratorio (lab data).
Los datos de campo provienen de usuarios reales y reflejan la gama real de dispositivos y redes; es aquello que debes optimizar. Los datos de laboratorio de Lighthouse o DevTools son reproducibles, lo que los hace mejores para diagnosticar un problema específico.
El navegador expone los tiempos reales a través de la Performance API.
// observe.js
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.name, entry.startTime);
}
}).observe({ type: "largest-contentful-paint", buffered: true });
Librerías como web-vitals envuelven estos observadores y envían los reportes a tu sistema de analíticas, lo que te permite obtener datos de campo sin tener que construir toda la infraestructura tú mismo.
Cargar menos
La palanca más importante es enviar menos. JavaScript es el recurso más costoso porque debe descargarse, analizarse y ejecutarse, y compite con el renderizado por el hilo principal.
- Elimina las dependencias que no utilices y prefiere alternativas más ligeras.
- Importa únicamente las funciones que necesites de una librería.
- Divide las rutas y las funcionalidades pesadas mediante
import()dinámicos. - Renderiza en el servidor siempre que sea posible y envía solo las partes interactivas.
- Comprime y sirve formatos modernos como Brotli y AVIF.
- Establece un presupuesto de bundle (bundle budget) y haz que la CI falle cuando este aumente.
Un analizador de bundles muestra exactamente qué hay en tu build, lo cual suele ser sorprendente la primera vez que lo revisas.
Carga diferida
No todo es necesario para el primer renderizado. Difiere el resto.
<!-- deferred.js -->
<script src="app.js" defer></script>
<script src="analytics.js" async></script>
<img src="hero.avif" fetchpriority="high" alt="Hero" />
<img src="thumb.avif" loading="lazy" decoding="async" width="400" height="300" alt="" />
defer ejecuta los scripts después del parseo, async los ejecuta tan pronto como se descargan, fetchpriority="high" prioriza la imagen LCP y loading="lazy" difiere las imágenes que están fuera de la pantalla. El import() dinámico hace lo mismo con los módulos de JavaScript.
Renderizado y estabilidad
Dos problemas de renderizado dominan el CLS y la velocidad percibida.
Reserva espacio para imágenes, iframes, anuncios y embeds para que no desplacen el contenido. Define width y height, o utiliza aspect-ratio en CSS. Implementa una estrategia de fuentes que evite el intercambio tardío (late swap), como font-display: swap con una fuente de respaldo compatible o fuentes autoalojadas.
Mantén el hilo principal libre. Las tareas largas bloquean la interacción e incrementan el INP. Divide el trabajo costoso, traslada los cálculos pesados a un Web Worker y evita el layout thrashing agrupando las lecturas y escrituras del DOM.
Caching
El caching convierte las visitas recurrentes en cargas instantáneas. Utiliza fingerprints en los assets mediante hashes de contenido y sírvelos con un max-age largo Cache-Control, ya que cualquier archivo modificado recibirá un nombre nuevo. Sirve el HTML con un cache corto y revalida. Para mejorar la velocidad en visitas recurrentes y el funcionamiento offline, un service worker puede cachear el app shell y los assets, tal como se explica en la guía de PWA.
Presupuestos de rendimiento
Un presupuesto convierte el rendimiento en una restricción en lugar de una esperanza.
{
"budgets": [
{ "path": "/*", "resourceSizes": [{ "resourceType": "script", "budget": 170 }] }
]
}
Elige un límite realista para JavaScript, imágenes y LCP, y luego aplícalo en CI. Cuando un pull request excede el presupuesto, la compilación falla y el autor decide si el compromiso vale la pena. Así es como los equipos evitan que el rendimiento se degrade con el tiempo.
Mejores prácticas
- Mide antes de optimizar y utiliza tanto datos de campo (field data) como de laboratorio (lab data).
- Envía menos JavaScript antes de realizar micro-optimizaciones en cualquier otra cosa.
- Implementa lazy-load en las imágenes que estén fuera del área visible (below-the-fold) y divide las rutas pesadas.
- Define dimensiones explícitas en los archivos multimedia para proteger el CLS.
- Utiliza
fetchpriority,deferyasyncde manera deliberada. - Aplica fingerprinting a los assets y almacénalos en caché durante un largo periodo de tiempo.
- Define y aplica un presupuesto de bundle (bundle budget) en el CI.
Errores comunes
- Optimizar la cosa equivocada sin realizar mediciones.
- Enviar un bundle de cliente enorme para contenido que podría ser estático.
- Cargar ansiosamente cada imagen y script.
- Omitir las dimensiones de las imágenes, provocando layout shift.
- Bloquear el hilo principal con tareas síncronas prolongadas.
- Tratar el rendimiento como un proyecto puntual en lugar de un presupuesto continuo.
Próximos pasos
El rendimiento es una disciplina, no una lista de tareas. Utiliza Vite para dividir y analizar tu bundle, reduce el peso de JavaScript con los patrones de la guía de React, añade almacenamiento en caché offline con PWA y mantén el hilo principal libre con un JavaScript sólido. Después, mide tu propio sitio con datos reales de campo y elige el cambio que tenga el mayor impacto.