Objetivo y requisitos
Cuando hablamos de Vapor Mode, casi siempre hablamos del compilador. Pero debajo de Vapor hay otra pieza que cambió en Vue 3.6: el sistema de reactividad, que ahora usa una implementación derivada de alien-signals. Lo bueno es que no tienes que cambiar tu código para aprovecharlo.
En este artículo vemos qué cambió, cómo medirlo sin engañarte y qué esperar en una aplicación real. Necesitas Vue 3.6.0-rc.15 (la última RC al escribir esto) y Node.js ^22.18.0 o >=24.12.0, el rango que usa create-vue. Para las mediciones, usé Node v24.21.0 en Windows 11.
npm install vue@3.6.0-rc.15
Si tu proyecto usa Vue Router 5, recuerda el overrides de los posts anteriores: el router declara peer vue ^3.5.34 y npm no acepta una RC dentro de ese rango.
Qué cambió por dentro
Vue 3.5 tenía un sistema de reactividad de pull: cuando cambia un ref, marca como sucios a sus dependientes, y un computed se recalcula solo cuando alguien lo lee. Ese diseño es correcto, pero en escenas con muchos computed leídos después de un mismo cambio, la forma en que Vue recorría y marcaba dependencias era cara.
En diciembre de 2024, el PR #12349 portó a @vue/reactivity el núcleo de alien-signals (versión 0.4.4). Según la descripción del PR, el cambio busca tres cosas:
Menos memoria. El autor reporta una reducción de alrededor del 13 % al crear muchos
ref,computedyeffect(de 2,3 MB a 2,0 MB en su prueba).Mejor rendimiento en lecturas masivas. El autor describe mejoras grandes en escenas donde muchos
computedse leen después de cambiar unref. Menciona más de 30 veces, proporcional a la escala, en un caso específico.Código más simple. La planificación ya no depende de detalles externos como la limpieza de dependencias o los eventos de depuración.
La API no cambia: ref, computed, watch y effect se escriben igual. Lo que cambia es cómo el sistema decide qué recalcular.
Este mismo paquete es la base de Vapor. Si abres runtime-vapor en 3.6.0-rc.15, verás que importa ReactiveEffect, Dep, startBatch y computed desde @vue/reactivity. Por eso el rendimiento de Vapor depende en parte de esta reactividad.
Mide la misma escena en las dos versiones
Las cifras del PR son del autor, en su máquina y su escena. Para saber qué pasa en la tuya, mide la misma escena en las dos versiones. Instala ambas versiones de @vue/reactivity con alias:
npm install r35@npm:@vue/reactivity@3.5.45 r36@npm:@vue/reactivity@3.6.0-rc.15
Luego escribe una escena que cambie un ref y lea muchos computed:
import * as v35 from 'r35'
import * as v36 from 'r36'
function scene(api, { computeds, refs, readAll, withEffect }) {
const { ref, computed, effect } = api
const sources = Array.from({ length: refs }, (_, i) => ref(i))
const comps = Array.from({ length: computeds }, (_, i) =>
computed(() => sources[i % refs].value * 2),
)
let sink = 0
if (withEffect) effect(() => { for (const c of comps) sink += c.value })
return () => {
sources[0].value++
if (readAll) for (const c of comps) sink += c.value
}
}
Cada operación se repite muchas veces, con calentamiento previo y la mediana de cinco rondas. Compara la misma escena con v35 y v36.
Los resultados en mi máquina
Estos son los tiempos por operación (en microsegundos, mediana de cinco rondas):
Escena | 3.5.45 | 3.6.0-rc.15 | Mejora |
|---|---|---|---|
1.000 computeds: escribir y leer todos (sin efecto) | 12,47 µs | 7,53 µs | 1,65× |
1.000 computeds: escribir y leer todos (con efecto) | 32,86 µs | 18,52 µs | 1,77× |
1.000 computeds: escribir sin leer (con efecto) | 19,28 µs | 14,53 µs | 1,33× |
1 computed: escribir y leer (sin efecto) | 0,05 µs | 0,04 µs | 1,08× |
Tres lecturas:
La mejora es real, pero es de 1,3× a 1,8×. Está lejos del 30× del PR. Ese número corresponde a una escena concreta a gran escala, y es distinto de lo que vas a notar en una aplicación.
Donde más se nota es en lecturas masivas. Cuando lees muchos
computeddespués de cada cambio, la mejora llega a 1,8×.Con un solo
computedno hay diferencia medible. El valor es 0,04 a 0,05 µs, cerca de la resolución deperformance.now(). No lo tomes como cifra.
Para la memoria no medí nada: el 13 % es del PR.
Lo que esto no significa
Estas son microbenchmarks de reactividad. No miden el render, el layout ni el paint. Una mejora de 1,8× en la reactividad se traduce en menos trabajo en el JavaScript, pero el tiempo que ve el usuario depende del resto de la pantalla. Lo vimos en el benchmark de Vapor: el layout del navegador se mantiene igual con los dos motores.
Tampoco hay que cambiar nada en tu código para obtener esta mejora. Eso es lo más valioso: si actualizas a 3.6 cuando salga la versión estable, tus computed y watch se benefician sin tocarlos. Pero también significa que no puedes medirla por cambios de código, sino por tus propias escenas.
Cómo medirlo en tu aplicación
Si quieres saber si la reactividad es el cuello de botella de una pantalla, no empieces por el benchmark. Primero mide la pantalla completa con la metodología del post de benchmark: build de producción, varias corridas, mediana. Si el tiempo está en el JavaScript de tus computed y no en el render, la reactividad puede importar. Si está en el render o en el layout, no.
Comprueba el resultado
Instala
r35yr36como arriba.Ejecuta la escena con tus números de
computedsyrefs, aproximándolos a los de tu pantalla.Repite la medición en build de producción, no en desarrollo.
Compara la mediana de varias rondas, no una sola corrida.
Errores frecuentes
Citar el 30× como si fuera tu mejora. Es el caso del autor. Mide tu escena.
Medir un solo
computed. La diferencia queda por debajo de la resolución del reloj.Medir en desarrollo. Las comprobaciones de Vue distorsionan los tiempos.
Atribuir a la reactividad cambios de render. Si la pantalla mejora, separa el JavaScript de la reactividad y del render antes de concluir.
Cuándo importa
Pantallas con muchos
computedderivados de pocosref. Tablas, dashboards, filtros.Listas con cambios frecuentes, donde cada cambio lee muchos valores derivados.
Antes de migrar a Vapor. La reactividad mejora ambos mundos; si ya tienes una pantalla con mucha reactividad, el beneficio se suma.
Si quieres ver cómo Vapor usa esta reactividad para actualizar solo el nodo afectado, el post sobre el compilador muestra la salida real.
Sigue leyendo
Una idea lleva a otra.
watch vs. watchEffect en Vue 3: evita cotizaciones de envío desactualizadasEvita respuestas HTTP desactualizadas con watch, debounce y cancelación en un cotizador de envíos.
¿Qué es un Composable?Extrae consultas HTTP a un composable tipado que cancela peticiones anteriores y maneja errores.- Vapor Mode en Vue 3.6: Vue sin Virtual DOM, explicado y medidoActiva Vapor Mode en Vue 3.6 con una sola palabra, mira qué compila y mide cuánto mejora frente al Virtual DOM, con cifras reales.