Inicio / Artículos

Presupuestos de rendimiento para JavaScript: Aplicación de límites en los paquetes en CI

Aprenda por qué JavaScript cuesta mucho más que su tiempo de descarga, cómo definir un presupuesto de rendimiento realista y cómo hacer que webpack rechace las compilaciones que lo superen.

Editor: REACTAPP.TOP7 min de lectura1503 palabras

Fuentes: The Cost of JavaScript — Performance budgets and why your bundle size is driving users away

Una página que parece responder al instante en la computadora portátil de un desarrollador puede congelarse durante segundos en el teléfono de un cliente real. La diferencia rara vez se debe únicamente a la velocidad de la red: proviene del trabajo que realiza la CPU para procesar cada kilobyte de JavaScript una vez que llega a un dispositivo modesto. Esta guía explica de dónde proviene ese costo, cómo convertirlo en un presupuesto de rendimiento explícito y cómo hacer que el pipeline de construcción rechace los cambios que exceden ese presupuesto antes de que lleguen a producción.

La diferencia entre la máquina del desarrollador y el dispositivo del usuario

La mayoría de los equipos front-end desarrollan y prueban en hardware que no es representativo en absoluto: computadoras portátiles recientes, conexiones por cable o fibra óptica, y un servidor de desarrollo que funciona en localhost. Bajo esas condiciones casi cualquier paquete parece rápido, por lo que el tamaño del paquete tiende a tratarse como algo que se puede optimizar más tarde en lugar de como una restricción de diseño.

El tráfico real es diferente. Una gran parte de los visitantes llega mediante teléfonos Android de gama media, conexión 4G deficiente o redes Wi-Fi públicas congestionadas. Imagina un panel de control que consume muchos datos y responde de inmediato en un navegador local, pero durante las pruebas en iPhones antiguos los controles interactivos tardan unos tres segundos en volverse utilizables. No hay nada roto en el código; simplemente el dispositivo necesita mucho más tiempo para procesar el mismo script.

Esa demora tiene un costo comercial. El JavaScript adicional y no optimizado retrasa constantemente el momento en que el usuario puede interactuar, y el efecto es especialmente doloroso en flujos críticos como iniciar sesión o agregar un artículo al carrito. Cuando esas acciones parecen lentas, la gente las abandona, y por eso un paquete excesivo puede alejar silenciosamente a los usuarios.

Por qué JavaScript cuesta más que su tiempo de descarga

Durante muchos años, la latencia de red fue el cuello de botella principal, y los esfuerzos por mejorar el rendimiento se centraron en reducir el tamaño de las imágenes y en optimizar los métodos de transmisión, como HTTP/2. Esas preocupaciones siguen siendo relevantes, pero en el caso de JavaScript, el mayor problema actual es lo que ocurre una vez que los datos llegan.

Un script no es utilizable hasta que finaliza su descarga. El navegador debe analizarlo, compilarlo y ejecutarlo, y gran parte de este proceso tiene lugar en el hilo principal, el mismo encargado de manejar toques, clics y la renderización. Es perfectamente posible que un paquete de 400 KB se descargue rápidamente y, aun así, hacer que la página parezca lenta, ya que el dispositivo está ocupado procesando código en lugar de responder a las entradas del usuario.

En un teléfono de gama media, este ciclo de análisis-compilación-ejecución puede afectar visiblemente a Interaction to Next Paint (INP), la métrica que mide la rapidez con la que una página responde a las entradas del usuario. Esto influye directamente en sus puntuaciones Core Web Vitals, y unos valores bajos suelen ir acompañados de una menor retención de usuarios y resultados comerciales más débiles. Por lo tanto, cualquier esfuerzo serio por mejorar estas métricas debe incluir la reducción de la cantidad de JavaScript que los dispositivos móviles deben analizar y ejecutar, no solo disminuir el tamaño de los archivos descargados.

Qué es un presupuesto de rendimiento y cómo establecerlo

Un presupuesto de rendimiento para JavaScript es un límite explícito sobre la cantidad de código que puede incluir una página, ruta o bloque. Técnicamente es solo un número, pero su verdadero valor es organizacional: representa un acuerdo al que se adhiere todo el equipo. En lugar de descubrir regresiones después del lanzamiento y tener que apresurarse a solucionarlas, el presupuesto actúa como una barrera que impide que se integren desde un principio.

Consejo práctico: Un presupuesto convierte el rendimiento, que normalmente es algo al que reaccionamos, en una restricción con la que diseñamos, al igual que ocurre con los requisitos de accesibilidad o seguridad.

Establecer un límite comienza con definir valores concretos y medibles. Un objetivo ampliamente utilizado es mantener el JavaScript del camino crítico de cualquier página por debajo de aproximadamente 170 KB comprimido, una cifra elegida para que un dispositivo móvil promedio pueda alcanzar rápidamente el estado interactivo. Debería validar cualquier número de este tipo según su propio público, pero el principio es el mismo: una vez establecido el límite, cada nueva dependencia o función debe justificar su tamaño desde el primer día.

Algunos puntos prácticos facilitan la gestión de los presupuestos:

  • Asignar un presupuesto por ruta o punto de entrada, y no solo para toda la aplicación, de modo que una área administrativa pesada no consuma todo el presupuesto destinado a la página de inicio.
  • Medir siempre de la misma manera (tamaño comprimido frente al tamaño bruto) para que las cifras sigan siendo comparables entre diferentes versiones.
  • Volver a revisar el límite cuando cambie la composición de su público o dispositivos, en lugar de aumentarlo silenciosamente cada vez que falla una versión.

Automatización de las verificaciones en CI/CD

Los presupuestos que dependen de que alguien recuerde revisarlos no duran mucho. Se envía una nueva función, un revisor se concentra en la lógica, nadie examina el informe del paquete y así se introduce una regresión. El único enfoque escalable es integrar directamente la verificación en el pipeline, de modo que cada cambio se evalúe automáticamente.

Nota: Al ejecutarse la verificación en CI, cada solicitud de integración se valida en relación con el presupuesto sin que nadie tenga que recordar revisarla.

Herramientas de compilación como webpack pueden hacer esto por usted: configúrelas para que traten los resultados de tamaño excesivo como un fallo en la compilación en lugar de una advertencia. De esta manera, la retroalimentación llega de inmediato, asociada al cambio que la causó. Si una solicitud de integración introduce una biblioteca que genera un fragmento por encima de su límite, el proceso se detiene y el autor debe solucionarlo antes de fusionarla.

La configuración a continuación utiliza la opción performance de webpack. Al establecer hints en 'error', las advertencias relacionadas con el tamaño se convierten en errores que impiden completar la compilación. maxAssetSize limita el tamaño de cada archivo generado (en este caso, 250 KiB), mientras que maxEntrypointSize limita el tamaño total de los archivos cargados para un único punto de entrada (en este caso, 500 KiB); ambos valores se expresan en bytes. La función assetFilter decide qué archivos se evalúan, en este caso omitiendo los mapas de origen y los archivos JSON para evitar alarmas falsas.

module.exports = {
  // ... other webpack configuration ...

  performance: {
    hints: 'error',
// Emit an error whenever a bundle exceeds the size limits
    maxAssetSize: 250 * 1024,
// Maximum size in bytes for any generated asset (e.g., 250 KiB)
    maxEntrypointSize: 500 * 1024,
// Maximum size in bytes for an entrypoint (e.g., 500 KiB)
    assetFilter: function(assetFilename) {

// Optionally exclude certain assets (e.g., source maps) from performance hints
      return !/\.(map|json)$/.test(assetFilename);
    }
  }
};

Tenga en cuenta que webpack compara estos límites con los tamaños de los archivos generados, que generalmente no son los tamaños comprimidos que descargan los usuarios. Si su presupuesto está definido en kilobytes comprimidos, conviértalo a tamaños sin comprimir para esta verificación, o combínelo con un paso separado que mida la salida en formato gzip o Brotli.

Estrategias para mantener los bundles dentro del presupuesto

Un presupuesto y una verificación automática le indican cuándo supera el límite; mantenerse por debajo de él requiere un diseño cuidadoso. Las técnicas más efectivas son bien conocidas y funcionan mejor cuando se combinan.

Dividir el código según rutas y funcionalidades

El split de código divide un paquete monolítico en fragmentos más pequeños que se cargan según sea necesario. Un caso común es un panel de control administrativo pesado dentro de una aplicación empresarial: al cargarlo mediante instrucciones dinámicas import(), su código solo se obtiene cuando un administrador navega realmente a esa ruta. Los usuarios regulares nunca lo descargan, lo que puede reducir cientos de kilobytes del payload inicial.

Deje que el “tree shaking” elimine el código muerto

El “tree shaking” elimina las exportaciones que nunca se utilizan, de modo que las partes no usadas de sus propios módulos y de las bibliotecas no terminan en el resultado final. Funciona mejor con la sintaxis de módulos ES y dependencias publicadas en un formato apto para el “tree shaking”, por lo que vale la pena verificar cómo se construye un paquete antes de confiar en él.

Cuidado: Incorporar una biblioteca grande solo para una función auxiliar es una de las causas más comunes de sobrecarga. Busque una alternativa más ligera o importe únicamente la función específica que necesite.

Posponga lo que la primera pantalla no necesita

El cargue diferido de componentes que no son esenciales para la vista inicial, como diálogos, widgets situados debajo del despliegue o paneles poco utilizados, pospone su costo de análisis y ejecución hasta el momento en que se requieran. Combinado con la división del código y el “tree shaking”, esto mantiene el hilo principal libre durante los primeros segundos, cuando es más probable que los usuarios interactúen.

Conclusión

  • La parte costosa de JavaScript en dispositivos móviles es su análisis, compilación y ejecución en el hilo principal, lo cual afecta directamente a INP y a los demás Core Web Vitals.
  • Un presupuesto de rendimiento es un acuerdo del equipo expresado en números, idealmente por ruta y medido de manera consistente.
  • Aplicar el presupuesto en CI, por ejemplo con performance.hints: 'error' de webpack, permite detectar regresiones en las solicitudes de integración que las introducen.
  • El dividir el código, el “tree shaking” y la carga diferida son formas de mantenerse dentro del presupuesto una vez que se establece; pruebe en dispositivos reales para confirmar que las cifras se traduzcan en una experiencia más rápida.
  • Lecturas relacionadas