Inicio / Artículos / Runes de Svelte 5 y señales de SolidJS: actualizaciones de la interfaz sin volver a renderizar

Runes de Svelte 5 y señales de SolidJS: actualizaciones de la interfaz sin volver a renderizar

Vea cómo Svelte 5 compila las runas y SolidJS conecta las señales para actualizar el DOM directamente, cómo eso difiere de React y Angular, y cuándo vale la pena hacer el cambio.

1774 palabras

Cada desarrollador de React termina teniendo que explicar por qué un componente se renderiza cuatro veces cuando no cambia nada visible, y por qué la solución implica el uso de memo, un array de dependencias y una función de callback estable. Svelte y SolidJS parten de una premisa diferente: si el framework sabe exactamente qué parte del estado alimenta qué parte del DOM, puede actualizar ese nodo y evitar ejecutar nuevamente los componentes. Este artículo explica cómo logran cada uno de ellos esto, qué aspecto tiene el código en la práctica, en qué se diferencian sus modelos de React y Angular, y cómo decidir si alguno de ellos encaja en tu próximo proyecto.

El DOM virtual era un medio, no un fin

La idea fundamental de React cuando se lanzó en 2013 fue el DOM virtual. Se describe la interfaz de usuario como una función del estado. Cuando el estado cambia, React llama nuevamente a tu componente, crea un árbol nuevo en memoria, lo compara con el anterior y aplica solo las diferencias al DOM real.

Ese diseño hizo que las interfaces fueran mucho más predecibles que la manipulación manual del DOM, y sigue siendo un modelo sólido. Sin embargo, conlleva un costo: la función del componente se ejecuta de nuevo independientemente de si su salida necesita cambiar, se asigna un árbol nuevo y se realiza una comparación para determinar qué cambió realmente, todo ello con el fin de actualizar un único <span> en el peor de los casos. Si deseas conocer los detalles de lo que compara el reconciler y por qué, el artículo sobre cómo funciona la comparación del DOM virtual los aborda.

Gran parte de la API de React desde entonces, memo, useMemo, useCallback y, más recientemente, el React Compiler, existe para evitar realizar las tareas que de otro modo haría el modelo de renderizado y comparación. El compilador automatiza la memorización, lo que permite a los desarrolladores escribir menos código manualmente, pero el modelo subyacente permanece igual: los componentes se vuelven a ejecutar, y la optimización consiste en convencerlos de no hacerlo. El artículo sobre qué optimiza el React Compiler y qué deja en manos del desarrollador analiza esos límites.

Svelte y Solid plantean una pregunta más simple: ¿qué pasaría si se conociera con precisión la dependencia entre cada parte del estado y cada nodo DOM, de modo que solo ese nodo se modificara cuando cambie el estado?

Svelte: un compilador que escribe el código de actualización por ti

Svelte es principalmente un compilador. Escribes los componentes en archivos .svelte, y en el momento de la compilación Svelte los convierte en JavaScript puro que manipula directamente el DOM. No existe un DOM virtual ni diferenciación en tiempo de ejecución, y solo se envía al navegador un pequeño módulo de tiempo de ejecución.

Desde Svelte 5, la reactividad se expresa a través de runas, primitivas explícitas que el compilador reconoce. El componente a continuación declara un estado y un valor derivado de él, y luego los muestra ambos en un botón:

<script>
  let count = $state(0);
  let doubled = $derived(count * 2);
</script>
<button onclick={() => count++}>
  {count} doubled is {doubled}
</button>

Ese es el componente completo. No existe función de establecimiento ni array de dependencias. Se incrementa count como si fuera una variable ordinaria, y dado que el compilador ya ha analizado qué partes del marcado leen count y doubled, genera código que actualiza exactamente esos nodos de texto. $derived se vuelve a calcular solo cuando cambia algo que lee. Cabe señalar que los manejadores de eventos en Svelte 5 son atributos normales como onclick, reemplazando la antigua sintaxis de directiva on:click.

Por qué a los equipos les gusta

  • Menos código para el mismo resultado. Sin ganchos, funciones de establecimiento ni componentes contenedores, los componentes de Svelte suelen ser notablemente más cortos que sus equivalentes en React. Menos código suele significar menos posibilidades de errores y revisiones más rápidas.
  • Paquetes pequeños. Dado que la mayor parte del trabajo se realiza en tiempo de compilación, el costo del framework en el navegador es bajo. Para sitios de contenido, páginas de destino y cualquier aplicación donde sea importante la primera carga, esto representa una verdadera ventaja comercial.
  • Bloques de construcción web familiares. El marcado, los estilos y los scripts se encuentran en un único archivo y se leen como HTML, CSS y JavaScript. No existen convenciones específicas de JSX como className, lo que hace que los archivos sean fáciles de entender para diseñadores y revisores que no escriben en React.
  • Para aplicaciones completas, SvelteKit añade enrutamiento, renderizado en servidor y puntos de extremo API, desempeñando el papel que Next.js tiene para React, con la reputación de requerir una configuración más sencilla.

    Hay una advertencia importante que vale la pena conocer desde el principio: las runas son características del compilador, por lo que solo funcionan dentro de archivos .svelte y en módulos nombrados con el sufijo .svelte.js o .svelte.ts. Trasladar la lógica reactiva a un archivo de utilidades común .js no funcionará sin ese tipo de nombre.

    SolidJS: JSX que se ejecuta una sola vez

    A primera vista, Solid puede confundirse fácilmente con React. Utiliza JSX, compone funciones pequeñas, y un contador se ve casi idéntico:

    function Counter() {
      const [count, setCount] = createSignal(0);
      return (
        <button onClick={() => setCount(count() + 1)}>
          Count: {count()}
        </button>
      );
    }
    

    La diferencia que sorprende a los desarrolladores de React es que Counter se ejecuta exactamente una vez. Solid se basa en una reactividad de alto nivel con señales. createSignal devuelve un getter y un setter, y el getter, count(), es una llamada a función en lugar de un valor simple. Cuando JSX lee count() dentro de una expresión, Solid registra que este nodo de texto en particular depende de esa señal. Al llamar a setCount posteriormente, solo se actualiza ese nodo de texto y nada más. La función del componente fue únicamente un paso de configuración que conectaba las señales con los nodos del DOM; nunca se ejecuta nuevamente, por lo que no hay nada que volver a renderizar.

    Este modelo es la razón por la cual Solid obtiene resultados en la cima o muy cerca de ella en las pruebas de rendimiento de los frameworks más comunes, a menudo comparables con el JavaScript puro escrito a mano. Considere cualquier clasificación en pruebas de rendimiento como un momento puntual y mida su propio volumen de trabajo; sin embargo, la ventaja arquitectónica es real: las actualizaciones cuestan aproximadamente en proporción a lo que cambió, y no al tamaño del árbol de componentes.

    Por qué a los equipos les gusta

    • No hay modelo mental de re-renderizado. No surge la pregunta típica en React sobre por qué algo se vuelve a renderizar. useMemo, useCallback y React.memo no tienen equivalentes porque no hay nada que se pueda omitir.
  • Efectos predecibles. Un efecto se ejecuta cuando cambia la señal que lee, no cuando un componente se vuelve a renderizar por casualidad y una matriz de dependencias lo permite. Las cierres obsoletos, un problema conocido en React, desaparecen en gran medida porque los valores siempre se leen de forma actualizada a través de los getters.
  • Una transición sencilla desde React. JSX y la composición de componentes se transfieren casi directamente, por lo que un equipo de React generalmente solo necesita dejar de usar las partes problemáticas en lugar de aprender todo de nuevo.
  • Los hábitos que debes dejar de usar

    El modelo de uso único tiene consecuencias que dificultan el trabajo a los principiantes. Al desestructurar las propiedades en la parte superior de un componente se leen sus valores una sola vez, lo que interrumpe la reactividad; por eso, las propiedades suelen accederse mediante props.name. Las devoluciones anticipadas y las estructuras ternarias en el cuerpo de la función se evalúan solo una vez, y por eso Solid ofrece componentes de flujo de control como Show y For. Una vez que se comprenden estas reglas, resultan coherentes, pero constituyen la principal fuente de errores para los desarrolladores provenientes de React.

    Comparación con React y Angular

    La diferencia más profunda es de naturaleza filosófica y no sintáctica.

    React ha reestructurado su modelo central con funciones de “ejecutar nuevamente y comparar diferencias” y ha dedicado años a añadir herramientas para reducir los costos asociados a dicho modelo. Sigue siendo una excelente opción: su ecosistema es insuperable, la contratación de personal es sencilla y el React Compiler realmente reduce la necesidad de memorización manual. La desventaja es que se trabaja dentro de un modelo diseñado según las limitaciones de su época.

    Angular es la opción empresarial con todas las funcionalidades, que incluye inyección de dependencias, RxJS y estrictas convenciones para casi todo. Maneja muy bien bases de código extensas, pero la complejidad y la curva de aprendizaje son reales. Sus cambios más recientes e importantes, como las señales y la detección de cambios sin zonas, lo acercan a una reactividad de alto nivel similar a la que popularizó Solid.

    Esa convergencia es visible en toda la industria. Angular adoptó los signals, React introdujo un compilador en tiempo de compilación, frameworks más recientes como Qwik se basan en una reactividad de alto nivel, y Vue, cuyos refs reactivos siempre estuvieron cercanos a los signals, ha estado explorando sus propias estrategias de compilación. Sería una exageración decir que Svelte y Solid inventaron cada una de estas ideas, pero demostraron desde temprano y de manera clara que la compilación y los signals pueden sostener todo un framework.

    Por qué los desarrolladores siguen avanzando en esta dirección

    Tres razones poco llamativas explican la mayor parte del interés:

    • La atención se dirige al producto en lugar a las semánticas de actualización del framework. Memorizar correctamente una función de callback no es considerado por nadie como un trabajo significativo.
  • Rendimiento por defecto. En React o Angular, lograr una aplicación rápida requiere un diseño cuidadoso. En Svelte o Solid, hacer que una aplicación sea lenta suele necesitar esfuerzo adicional.
  • Respeto por su tiempo. APIs más pequeñas, menos código genérico y menos trampas. Las encuestas a desarrolladores han situado repetidamente a ambos frameworks en posiciones altas en cuanto a satisfacción e interés, aunque su uso general sigue creciendo partiendo de una base relativamente pequeña.
  • ¿Debería cambiar?

    En el caso de un producto ya existente, casi con certeza no de inmediato. Cuando hay una aplicación importante en ejecución con React o Angular y el equipo domina bien esa tecnología, reescribirla es una de las formas más seguras de retrasar un proyecto. El tamaño del ecosistema también es importante: React cuenta con una biblioteca para casi cualquier necesidad, y aunque los ecosistemas de Svelte y Solid son sólidos y en crecimiento, son más pequeños; por lo tanto, verifique con antelación que las herramientas de las que depende, como bibliotecas de componentes, herramientas para formularios e integraciones de autenticación, existan y estén actualizadas.

    El cálculo cambia para nuevos proyectos. Un proyecto desde cero, un widget sensible al rendimiento o una herramienta interna son opciones de bajo riesgo para probarlo:

    • elija Svelte si desea una curva de aprendizaje sencilla y una experiencia que funcione casi sin problemas
  • Elige Solid si tu equipo piensa en JSX y desea el máximo rendimiento en tiempo de ejecución con un modelo mental similar al de React
  • Mantente con React o Angular cuando la amplitud del ecosistema, la posibilidad de contratar talento y la experiencia existente son más importantes que la eficiencia bruta
  • Puntos clave

    • El modelo de renderizado y diferenciación de React es predecible, pero el trabajo realizado es proporcional al árbol de componentes; la memorización y el Compilador de React reducen ese trabajo sin cambiar el modelo.
    • Svelte 5 traslada la reactividad al compilador mediante runas como $state y $derived, generando actualizaciones directas del DOM y ofreciendo un runtime pequeño.
    • Solid ejecuta cada componente una sola vez y vincula las señales directamente a los nodos del DOM, lo que evita la re-renderización pero requiere nuevos hábitos en cuanto a props y flujo de control.
    • El ecosistema más amplio se está alineando hacia las mismas ideas: análisis en tiempo de compilación, señales en lugar de volver a renderizar y actualizaciones directas en vez de comparar cambios.
    • Acepte estos frameworks donde sus ventajas sean relevantes y el ecosistema cubra sus necesidades; no reescriba una base de código sólida solo para seguir la tendencia.