Rendimiento de AfterPack: velocidad y tamaño medidos

Lo que AfterPack cuesta a un build y a una página: tiempo de ofuscación medido sobre un bundle de 185 KB, tiempo y tamaño de descarga del build wasm, tamaño de salida, tiempo de arranque de la página y coste en tiempo de ejecución de cada preset, lo que paga el código protegido al leer un global del host y cómo afecta una semilla nueva a la caché.

Cada número de esta página se midió, no se estimó. Salvo que una tabla diga lo contrario, la máquina era un Apple M2 Max, 32 GB de RAM, macOS 26.6.2. Nada de esto es una comparación con otra herramienta.

El fixture detrás de la mayor parte es next16_framework_runtime: 185 KB del runtime del framework que publica un build real de Next.js 16. Es un único bundle de producción ya minificado, que es la forma de código sobre la que AfterPack se ejecuta realmente.

Tiempo de build

Medido el 2026-09-29, en la máquina indicada arriba, con AfterPack 0.2.0 en el preset light por defecto.

Qué se ejecutóEntradaTiempoRendimiento
El paquete npm, sobre el bundle de 185 KB185 KB229 ms0.79 MB/s
El motor por sí solo, mismo bundle185 KB243 ms0.74 MB/s
El paquete npm, sobre un build de varios archivos de 766 KB766 KB1,362 ms0.55 MB/s

La fila de npm es la que debes usar para planificar. Es el paquete publicado haciendo el trabajo igual que lo hace un plugin o npx afterpack@latest, así que incluye el coste de llevar tu código al motor y sacarlo de vuelta. La fila del motor es el mismo bundle con el mismo preset sin ese coste, cronometrado con un arnés de medición aparte. Las dos quedan a menos de 15 ms una de otra, así que el empaquetado no añade un coste medible.

El pico de memoria en la fila del motor fue de 20.8 MB por encima de la base para un único archivo de 185 KB. La ejecución de varios archivos de 766 KB llegó a 76 MB en su punto máximo.

La ofuscación es un paso posterior al build, así que este tiempo se suma a tu build, no a la carga de página de tus usuarios. Un build de 766 KB en aproximadamente 1.4 segundos es el orden de magnitud del coste con el preset por defecto; un preset más alto cuesta más.

El build wasm

@afterpack/wasm es el mismo motor compilado a WebAssembly, para un Cloudflare Worker, para Node y para el playground de este sitio.

Tiempo, por presetNode 24
Bundle de 110 KB, light147 ms
Bundle de 185 KB, light237 ms
Bundle de 185 KB, medium1,011 ms
Bundle de 185 KB, extreme2,603 ms

Cada tiempo es la mediana de nueve ejecuciones con el Protection Map activado, medida el 2026-10-17 con @afterpack/wasm 0.2.6 en la máquina indicada arriba. El bundle de 110 KB es el chunk de polyfills del mismo build de Next.js 16.

La descarga ocupa 4.36 MiB sin comprimir y 1.25 MiB con gzip. Ocurre una vez por isolate de Worker o por pestaña del navegador, no una vez por build.

La velocidad se hace cumplir: una comprobación de CI cronometra el motor sobre el fixture de 185 KB con el preset extreme y el Protection Map activado, junto a un motor de referencia fijado en la misma ejecución, y hace fallar el build si la ejecución más rápida del motor tarda más de 13.5 veces lo que tarda la de la referencia.

Wasm es más lento que el build nativo, y su velocidad es una concesión deliberada: un build anterior optimizado para tamaño salía un 39% más pequeño con gzip y 2.1x más lento, lo que llevó un build real de 66 archivos más allá del plazo límite del motor en la nube. Ganó la velocidad.

Son tiempos de un portátil, no de Cloudflare. Un isolate de Worker en la red de Cloudflare tiene menos CPU, una caché más fría y un límite de CPU por petición, así que la misma entrada tarda más allí de lo que dice esta tabla. Mide en la plataforma en la que vas a ejecutarlo antes de dimensionar un presupuesto con estos números.

Tamaño de salida

La ofuscación añade bytes. Medido el 2026-10-16 con AfterPack 0.2.6, sobre cinco fixtures (el runtime de 185 KB de arriba, otros dos chunks de Next.js 16, un bundle de React con Vite y un chunk de PostHog), por preset, con la salida comprimida con gzip frente al mismo archivo minificado:

PresetMultiplicador de la salida con gzip
minifyde 1.09x a 1.17x
lightde 1.29x a 1.60x
mediumde 6.04x a 8.33x
hardde 6.62x a 9.23x
extremede 8.25x a 11.19x

Cada rango es la dispersión entre los fixtures, no un intervalo de confianza, y aumenta de forma monótona con el preset.

El objetivo de complejidad fija el tamaño de salida; estos rangos son mediciones de lo que produjo cada preset. Establece un inflation.max explícito cuando quieras que un build falle en lugar de superar un tamaño que elijas; consulta Presets.

Los archivos pequeños salen proporcionalmente más grandes que cualquiera de estos valores, porque toda salida lleva la sobrecarga fija del runtime del motor. Mide la proporción sobre un bundle real.

Carga de página

Un preset más pesado hace que una página tarde más en arrancar, porque el navegador analiza y ejecuta más código antes de que la página responda. Medido el 2026-10-24 con AfterPack 1.0.0, en la máquina indicada arriba, en Chromium headless: cinco apps pequeñas de los tests de extremo a extremo de los plugins de AfterPack, cada una construida una vez sin AfterPack y una vez por preset con todo el bundle en ese preset, y servida desde localhost, así que no se incluye tiempo de red. Cada cifra es el tiempo desde la navegación hasta el primer clic sobre el contador de la app que lo cambia, la mediana de cinco cargas en frío, cada una en un contexto de navegador nuevo.

AppSin AfterPacklightmediumhardextreme
Vite y React35 ms52 ms253 ms295 ms596 ms
Vue sobre Vite31 ms36 ms176 ms117 ms199 ms
Islas de Astro44 ms48 ms205 ms249 ms622 ms
SvelteKit27 ms32 ms84 ms89 ms162 ms
Next.js, webpack42 ms74 ms462 ms573 ms1,010 ms
Next.js, webpack, CPU limitada 4x145 ms234 ms1,652 ms2,215 ms3,844 ms

La última fila hace la CPU de Chromium cuatro veces más lenta, una aproximación a un móvil de gama media.

Sin protección, las páginas cargan de 168 KB a 453 KB de JavaScript. El tamaño crece aproximadamente en el mismo factor en todas las apps (las proporciones están en Presets); el tiempo de arranque crece según cuánto de ese código se ejecuta antes del primer clic. El tiempo de script en el hilo principal en medium fue 4.5x el del build sin proteger en Vue y 14.7x en Next.js. El runtime del propio framework forma parte de cada bundle, y todos los presets también lo protegen.

En light, el primer clic que funciona llegó de 4 ms a 32 ms más tarde. A partir de medium, mide tus propias páginas antes de publicar el preset en todo el bundle; lo que cada nivel te permite acotar está en Presets.

Coste en tiempo de ejecución

El código protegido se ejecuta más despacio que el original, y el coste sube con el preset. Medido con AfterPack 0.2.4 en el runner de CI x86_64 autoalojado, Node 22: cuatro programas de bucles cerrados; cada cifra es el tiempo de ejecución protegido dividido por el del original, la mediana de los cuatro.

PresetTiempo de ejecución frente al original
minify1.0x
light2.1x
medium32.5x
hard35.0x
extreme37.3x

Son el peor caso: los bucles no hacen más que aritmética protegida, y el código de una aplicación normal pasa mucho menos tiempo en bucles así. Una comprobación de CI hace fallar el motor si la mediana de un preset supera 2.5 veces su cifra de aquí.

Un núcleo numérico caliente, como un filtro de imagen, un paso de física o el bucle interno de un parser, paga casi la cifra completa. Con una clave Pro, mantenlo en light con un preset de región y deja el resto del bundle en tu preset global. Un build Free ignora un preset de región más bajo, así que en Free mantén el preset global en light cuando importe un núcleo caliente.

Lecturas de globales del host

El código protegido lee una variable global del host, como URL, a través de una comprobación que lanza el mismo ReferenceError que tu código original cuando esa global no existe. Lo que cuesta la comprobación depende de dónde se ejecuta el código.

Medido el 2026-10-13 con AfterPack 0.2.4, en Node 24 sobre un Mac arm64, con un bucle que lee URL una vez por iteración. Cada cifra es el tiempo de ejecución del build protegido dividido entre el del mismo build protegido leyendo la global sin la comprobación. Un rango abarca dos semillas, cada una la más rápida de 5 a 7 rondas intercaladas.

PresetContexto ordinarioDentro de node:vm
light1.00x2.7x
mediumde 1.06x a 1.13xde 2.0x a 2.1x
hardde 1.04x a 1.05xde 1.9x a 2.1x
extremede 1.10x a 1.18xde 2.0x a 2.1x

Un contexto ordinario es una página del navegador, un worker o el contexto principal de Node; esta columna se midió en un worker thread de Node. Ahí la comprobación no cuesta nada medible en light. A partir de medium, la protección añadida del preset envuelve la comprobación, y eso se paga en cada lectura.

Un contexto node:vm pasa cada búsqueda en su objeto global por un callback, así que ahí el mismo bucle tarda de 1.9x a 2.7x lo que tarda sin la comprobación. Un test o benchmark que ejecuta código protegido dentro de node:vm ve este coste en su límite superior, así que mide el código que lee globales del host en un bucle caliente donde lo ejecutan tus usuarios.

Caché entre releases

Una semilla nueva cambia los bytes de cada chunk protegido, incluidos los chunks cuyo código fuente no cambió. Los plugins de bundler incorporan la semilla al nombre de archivo de cada chunk, así que tras un release los visitantes que vuelven descargan de nuevo todo el JavaScript protegido. Una semilla fija mantiene un chunk sin cambios idéntico byte a byte y en caché, y ese chunk conserva entonces su forma entre releases. Con salida de Next.js, Angular, Vite o webpack, la CLI renombra los archivos con hash de contenido igual que los plugins; con cualquier otra salida los nombres se mantienen, así que fija seed o da a esa salida una vida de caché corta. Consulta Nombres de archivo y caché.

Siguiente

  • Presets: el presupuesto de tamaño que fija cada preset y lo que cuesta subirlo.
  • Build y CI: dónde encaja el paso en un pipeline.
  • Cloudflare Workers: ejecutar el build wasm en un isolate.
  • Playground: el build wasm, ejecutándose en tu navegador y cronometrándose a sí mismo.