SolidJS: El Framework reactivo

0
1011

En los últimos años, ha surgido una nueva oleada de frameworks que buscan competir con los ya conocidos en el desarrollo web, como Angular, React y Vue. Entre estos nuevos frameworks se encuentran Astro y Qwik, del que he hablado en otro artículo. Sin embargo, en este artículo nos centraremos en SolidJS, un framework que ha ganado notable popularidad entre los desarrolladores que buscan una alternativa más liviana y moderna a las opciones establecidas.

Índice de contenidos

  1. ¿Qué es SolidJS?
  2. Caracerísticas
  3. Reactividad
  4. Comparativa con otros Framework
  5. Prueba práctica
  6. Conclusiones
  7. Referencias

1. ¿Que es SolidJS?

Se trata de un librería de JavaScript open source creada por Ryan Carniato, y lanzada por primera vez en 2020. Se enfoca en el rendimiento y la escalabilidad. Está diseñada para ayudar a los desarrolladores a crear aplicaciones web rápidas, eficientes y reutilizables. Combina la sintaxis y la experiencia de desarrollo de React con el rendimiento de Frameworks como Svelte. Permite a los desarrolladores construir UIs con herramientas para mejorar los componentes de sus aplicaciones con reactividad, que es código JavaScript declarativo que vincula la interfaz de usuario con los datos que crea y utiliza. Está construido sobre herramientas establecidas como JSX y TypeScript , y está integrado con el ecosistema de Vite.

En SolidJS los componentes solo se ejecutan en una ocasión, que es cuando se renderizan por primera vez, en el caso de los hooks y bindings, solo cuando sus dependencias se actualizan.

Otro punto clave de SolidJS, es que en lugar de usar un Virtual DOM, compila sus plantillas a nodos reales del DOM y los actualiza con reactividad precisa. El compilador de código de SolidJS transforma el JSX en funciones que crean y actualizan los nodos del DOM según el estado reactivo.

Puntos clave de Solid

  • Soporte a TypeScript
  • Soporte a JSX
  • Server-side Rendering
  • Renderizado asíncrono
  • Tamaño de librería pequeño (7.9KB minified + gziped)
  • Fácil de debuggear / perfilar (Profiling)
  • Programación reactiva y declarativa

2. Características

SolidJS puede parecer una alternativa a React, pero es mas que eso, cuenta con una serie de características y ventajas únicas.

Reactividad precisa

SolidJS hace uso de una reactividad precisa. Utiliza este sistema para realizar un seguimiento de los cambios individuales en el estado de la aplicación a un nivel muy preciso, llevando a actualizaciones rápidas y eficientes.

Esto permite a SolidJS actualizar únicamente las partes de la interfaz de usuario que realmente necesitan un cambio, sin la necesidad de tener que renderizar de nuevo todo el componente cada vez que ocurra un cambio en el mismo, como ocurre con React.

Esta reactividad se consigue gracias a una técnica llamada programación reactiva, que ha formado parte del núcleo del Framework desde el inicio. Es usada automáticamente para actualizar la UI cada vez que cambian los datos, esto ayuda a reducir el tiempo de desarrollo, ya que los desarrolladores no van a tener que crear código extra para la gestión de la actualización de datos.

Además de eso, introduce mejoras en el compilador, y cuenta con un compilador que es capaz de optimizar el código durante el proceso de construcción de la página, consiguiendo con esto tener componentes más pequeños y mucho mas rápidos en un tiempo de ejecución mínimo.

Al contrario que React, no hace uso de un Virtual DOM. SolidJS elimina la necesidad del mismo siguiendo un enfoque de ejecución en tiempo de compilación, reduciendo el uso de memoria y mejorando el rendimiento.

Mejoras en el rendimiento

Los beneficios de la reactividad precisa en el rendimiento se hacen notar en benchmarks y en aplicaciones web que existan actualmente. El equipo de desarrollo de SolidJS explica que el motor de reactividad es mucho más rápido que el de otros Frameworks populares como React o Vue, especialmente en escenarios donde los componentes cuentan con una gran cantidad de datos y lógica.

Sin embarco, el equipo de SolidJS también comenta que el rendimiento no es el único beneficio de la programación reactiva. Con la actualización automática de la UI en base a los cambios, SolidJS hace que el desarrollo sea más rápido y más fácil, ya que los desarrolladores no van a necesitar escribir código para la gestión de cambios en el estado y actualizaciones en la UI.

Imagen de la comparativa de rendimiento entre Frameworks
Comparativa de rendimiento en navegador entre Frameworks de JavaScript

3. Reactividad

Lo primero que deberíamos hacer sería conocer el concepto de reactividad, que es a la capacidad de reacción y respuesta a los cambios en los datos o estado de la aplicación. Por ejemplo, en el caso de React, en lugar de tener que manipular manualmente los elementos del DOM para reflejar los cambios, este se encarga de renderizar de nuevo el componente que contiene los datos cuando estos cambian.

En JavaScript, la reactividad no es una característica integrada, lo que significa que cuando el estado cambia, la lógica y la vista asociadas no se actualizan automáticamente. Es tarea del Framework o del desarrollador garantizar la sincronización entre el modelo y la vista.

Lo que propone SolidJS en este caso es renderizar el componente una única vez, los cambios de datos o estado son controlados por los reactive primitives de SolidJS, que nos devuelven un getter y un setter. Lo que pasará con el componente a continuación, es que observará los datos de este primitivo y en caso de que el mismo cambie, solo se modificará donde esté localizado el mismo en el DOM, en lugar de renderizar de nuevo todo el componente, como ocurre con React.

SolidJS Reactive primitives

Esta reactividad precisa de SolidJS se consigue gracias a los primitivos que nos ofrece el mismo, que serían los siguientes:

Signals

Las signals se tratan del primitivo más importante dentro del sistema reactivo de SolidJS. Estas contienen un valor, y unas funciones getter y setter, que nos permiten saber en qué momento son son leídas o modificadas.

El valor que contienen va cambiando. Y en cuanto se actualice el valor de esta signal, todos los elementos que dependen de la misma se verán actualizados también.

const [count, setCount] = createSignal(0);  

Se podrían considerar como emisores de eventos (event emitter) que cuentan con una lista de suscripciones. Y notifican a sus suscriptores cada vez que cambia su valor.

Lo interesante es como suceden estas suscripciones. SolidJS hace uso de un seguimiento de dependencias automático, donde las actualizaciones se dan cada vez que cambian los datos.

El truco está en el stack global que existe en tiempo de compilación. Cada vez que se ejecuta (o re-ejecuta) una función que depende del valor de una signal, esta función se añade a ese stack. Cada signal que se lee comprueba si existe un listener en este stack y si es así, lo añade a la lista de suscripciones.

Effects

Los effects son funciones que envuelven las lecturas de las signal y que se re-ejecutan cada vez que esa signal de la cual es dependiente actualiza su valor.

Las signal son valores trackeables, y para complementar a estos son necesarios unos observer, que van a ser actualizados en base a esos valores. Un effect es el observador, que ejecuta un side effect que depende de la signal en sí.

Un punto importante es que estas signal pueden transportar cualquier tipo de datos y los effect pueden hacer cualquier cosa con ellos.

Otro punto sería que, las actualizaciones, se producen de manera síncrona. Antes de poder registrar la siguiente instrucción, el effect ya se ha ejecutado.

createEffect(() => console.log("The latest count is", count()));  

La mayoría del comportamiento en SolidJS se crea solo con estos dos primitivos que hemos mencionado. Sin embargo, hay otro primitivo básico del que necesitamos hablar.

Memos

Antes de explicar el siguiente primitivo, habría que hablar de otro concepto en SolidJS, que es el de las derived signals, se llama así a funciones que envuelven una signal, como podría ser esta.

const [count, setCount] = createSignal(0);  
const doubleCount = () => count() * 2;  

Esta función que envuelve a una signal se llama derived signal, y al envolver a una signal, pasa a ser una signal también, por lo que cada vez que se actualice el valor de la signal que envuelve, el resultado de esta función también se actualizará.

Hay momentos en los que queremos utilizar nuestros datos de distintas maneras usando la misma signal y acabamos llamando a la misma derived signal con los mismos datos varias veces. Normalmente no habría problema en llamarla varias veces, el caso es que se pueden dar situaciones en las que llamar nuestra derived signal de manera repetida puede llegar a tener un coste alto en el rendimiento. Por lo que nos podría interesar cachear el resultado de esa función para no tener que ejecutarla de nuevo y reducir el trabajo duplicado, a no ser que las dependencias dentro de la misma cambien.

Para esto hacemos uso de las funciones memos, el último tipo de primitivo, que vendrían a ser valores derivados cacheados. Estos comparten las particularidades de las signal y los effects. Hacen un seguimiento de las signal de las que dependen, y solo se re-ejecutan cuando estas cambian. Por sí mismas, también vendrían a ser signal trackeables.

Los memos son tanto un observer como una signal de solo lectura. Ya que son conscientes de tanto sus dependencias como sus observer, y pueden asegurarse de que solo se ejecutan una vez por cada cambio. Esto las hace más preferibles a la hora de registrar effects para escribir signals.

const doubleCount = createMemo(() => count() * 2);  

4. Comparativa con otros Framework

En este apartado haremos una comparativa con los dos Framework con los que más se relaciona a SolidJS debido a sus puntos en común, React y Svelte.

React

Aunque SolidJS se alinea en gran medida con la filosofía de diseño de React, funciona de manera fundamentalmente diferente. En esta sección veremos algunas de las diferencias entre las mismas.

Ausencia de un Virtual DOM

Una de las diferencias más notables entre SolidJS y React es la ausencia de un DOM virtual. Existe la idea de que el uso del DOM principal hace que nuestras aplicaciones se ralenticen, y precisamente el uso del mismo es parte de lo que hace que SolidJS tenga un rendimiento tan alto.

El uso del Virtual DOM es una solución que muchas librerías y Frameworks, como React, Vue.js y Riot.js, utilizan. Sin embargo, hoy en día, los creadores de Svelte y SolidJS han descrito el Virtual DOM como un extra que solo ralentiza el rendimiento de la librería. Y han buscado opciones alternativas que son más rápidas, y estas hacen uso del DOM real.

La forma en que SolidJS ha logrado obtener velocidades tan altas sin usar un Virtual DOM es compilando sus plantillas en nodos del DOM real y controlando las actualizaciones que se producen dentro de estas con reactividad precisa. De esta manera, cuando se actualiza el estado de la aplicación, solo se ejecuta el código que depende del mismo.

Los componentes no se vuelven a renderizar

En Solid, a diferencia de React, los componentes se renderizan solo una vez. Las expresiones JSX y primitivos que son utilizados en el componente son los que se actualizan.

Esto brinda rendimiento a SolidJS, ya que no es necesario volver a renderizar un componente cada vez que es utilizado. SolidJS es lo suficientemente reactivo como para ser capaz de rastrear los cambios que ocurren dentro del propio componente por sí mismo.

Reactividad detallada

React en sí mismo no es completamente "reactivo", esto es algo reconocido por el equipo de React.

Svelte

SolidJS a menudo se compara con Svelte, otra biblioteca de Javascript popular increíblemente rápida.

De hecho, tanto SolidJS como Svelte se consideran similares y están entre los frameworks de JavaScript más rápidos ya que ambos utilizan un enfoque de programación reactiva y una técnica de optimización en tiempo de compilación para lograr un alto rendimiento y una mínima sobrecarga.

Tanto SolidJS como Svelte utilizan una técnica de optimización en tiempo de compilación para minimizar el tamaño del código de la aplicación final. SolidJS utiliza un compilador basado en TypeScript que analiza el código en tiempo de compilación y genera código JavaScript optimizado eliminando el código innecesario y mejorando el rendimiento. Svelte, por otro lado, utiliza una técnica llamada "Svelte compiler" que analiza el código y genera código JavaScript vainilla altamente optimizado.

SolidJS no utiliza un compilador JavaScript por separado. Si no que aprovecha directamente el entorno de tiempo de ejecución de JavaScript para interpretar y ejecutar su código.

SolidJS es un Framework en el que su código se ejecuta en tiempo de ejecución a través del motor JavaScript en el navegador o en el servidor. Proporciona un conjunto de APIs y herramientas que los desarrolladores pueden utilizar para construir componentes y aplicaciones reactivas usando una sintaxis declarativa. El código de SolidJS se escribe en TypeScript o JavaScript, y se compila con el compilador de TypeScript, en caso de utilizarse este, para producir código JavaScript que puede ser ejecutado por el motor del mismo.

Por último, y no por ello menos importante, SolidJS proporciona soporte integrado para el renderizado en el lado del servidor (SSR), lo que puede mejorar el tiempo de carga inicial y el SEO de tu aplicación web.

5. Prueba práctica

Para la parte práctica de esta aplicación se ha decidido crear una aplicación básica, para ver cómo se trabaja con el Framework desde un nivel básico haciendo uso de los primitivos que ofrece.

Lo primero que habrá que hacer será ejecutar el comando para generar nuestra aplicación SolidJS, necesitaremos npm para poder ejecutar el comando.

npx degit solidjs/templates/ts  

Una vez creada la aplicación contaremos con la siguiente estructura de carpetas.

Imagen con la estructura de código de SolidJS

Trabajaremos directamente en la carpeta de App.tsx para crear nuestra aplicación Todo, ya que se trata de la carpeta principal. Una de las primeras cosas que podemos apreciar, es que el proceso de creación de Componentes es muy similar a react, creando el componente App como una función.

const App: Component = () =>; {}

Signal

Lo primero que haremos para nuestra aplicación Todo será crear una signal para guardar los datos de las tareas.

const [todos, setTodos] = createSignal([]);

Este proceso es muy similar a React, donde definimos un useState para guardar los datos, y recibimos una tupla de getter y setter, aunque sean similares, por detrás trabajan de manera diferente.

Lo siguiente que vamos a hacer será crear una función para ir añadiendo tareas.

const addTodo = (text: string) => {
    setTodos([...todos(), { id: ++todoId, text }]);
  }

Luego escribiremos el código HTML para pintar nuestra lista de tareas.

Imagen de app todo básica con una tarea

Effects

Ahora haremos uso de otro los primitivos que nos ofrece SolidJS, vamos a añadir un effect que nos pinte el número de tareas que tenemos en este momento.

createEffect(() =>; {
    console.log( `${todos().length} tasks`)
})

Hemos creado un effect, el proceso es similar a React, pero se diferencia en que no vamos a tener que escribir un array de dependencias. SolidJS detecta todas las signals de las que depende el estado y ejecutará él mismo en base a los cambios que hayan en estas.

Imagen de aplicación todo con dos tareas y mostrando por consola el número de las mismas

Memos

El último primitivo que voy a probar es el de las funciones memos, para mostrar cómo funcionan voy a crear un botón que se encargue de filtrar las tareas que no han sido completadas, para realizar el filtrado de tareas, he creado la siguiente función.

const filterUnfinished = () => {
        console.log('showing unfinished tasks...')
        return todos().filter(x => !x.finished)
    })

He añadido dentro de la función un console.log para ver cuántas veces entramos en la misma.

Imagen de aplicación todo básica mostrando por consola que se ha pintado tres veces el mismo texto

Si hacemos varias veces click en el botón, veremos que hay una llamada a la función por cada click que hacemos, a pesar de que no ha cambiado el número de tareas, que es de lo que depende esta derived signal. En este punto es interesante hacer uso de las funciones memos, ya que haciendo uso de estas conseguimos cacheamos el resultado de la función.

const filterUnfinished = createMemo(() => {
        console.log('showing unfinished tasks...')
        return todos().filter(x => !x.finished)
    })

Y así, hasta que se actualicen las dependencias de la misma, la función no se ejecutará de nuevo. Los memos son conscientes de sus dependencias y sus observer, esto los hace capaces de controlar que solo sean ejecutados una vez por cada cambio.

Imagen de aplicación todo básica mostrando por consola un solo mensaje

Adjunto un enlace a CodeSandbox para el que quiera ver el código.

6. Conclusiones

Se trata de una librería de JavaScript declarativa, eficiente y flexible que ofrece una alternativa a los frameworks basados en Virtual DOM, como React o Vue. Utiliza un sistema de reactividad precisa que permite actualizar el DOM real de forma selectiva y rápida, sin necesidad de renderizar todo el componente. También ofrece características modernas como JSX, Context, Portals, Suspense, SSR, hidratación progresiva y renderizado concurrente. 

Sin embargo, SolidJS también tiene una curva de aprendizaje elevada ya que introduce nuevos conceptos y requiere una nueva forma de pensar para los desarrolladores, por lo que es un Framework más adecuado para desarrolladores que tengan algo de experiencia. 

Además, SolidJS es un proyecto relativamente nuevo y no cuenta con un ecosistema tan grande como el de sus competidores que llevan más tiempo en el mercado, por lo que la documentación no va a ser la más completa y no va a contar tampoco con una gran cantidad de herramientas o plugins. Aún así, la comunidad le esta dando bastante apoyo y la misma está creciendo rápidamente y no creo que vaya a ser un problema que dure mucho tiempo.

En conclusión, considero que SolidJS es una biblioteca que ofrece numerosas opciones para construir aplicaciones web modernas de gran escala y altamente optimizadas. Creo que, junto con Qwik y otras bibliotecas que están ganando popularidad en la actualidad, tiene el potencial de establecerse como una opción destacada en el campo del desarrollo web.

7. Referencias

Documentación de SolidJS
Artículo hablando de la programación reactiva
Artículo hablando sobre SolidJS
Artículo que hace una comparativa entre SolidJS y React

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad