Implementación del Virtual DOM
En la prueba definida en el documento frontend test.docx
, se pedía realizar una interfaz para una aplicación web, capaz de administrar una base de datos en la que hay guardados una serie de registros de usuarios y roles.
Uno de los condicionantes del test era que no podía utilizar ningún framework ni librería. En un primer momento pensé en realizar una interfaz básica y definir la funcionalidad de la aplicación a partir de event listeners y controladores alrededor de los inputs de la interfaz, pero, ya que la finalidad de la prueba era comprobar mis conocimientos de JavaScript, decidí afrontar el reto de desarrollar, desde cero, un framework de web components y mi propia implementación del Virtual DOM, consiguiendo así una funcionalidad semejante a la que ofrecen Angular, React, Vue, Polymer o Ember, entre otros.
Mis conocimientos de JavaScript son limitados, mi primer contacto con este lenguaje de programación ocurrió hace unos 2 años, aun así, se ha convertido en uno de los lenguajes con los que más disfruto trabajando. Nunca antes había desarrollado un framework de estas condiciones, y aunque ha requerido mucha investigación y prueba y error, he terminado más que satisfecho con el resultado.
Obviamente mi framework no ofrece todas las utilidades que puede ofrecer cualquier framework de las anteriormente nombrados, ya que he ido desarrollándolas a medida que iba necesitando utilizarlas.
A través del código sigo una serie de convenciones a la hora de nombrar atributos, algunas de las más importantes son:
_
: un atributo prefijado con_
será reconocido como un atributo privado, el cual no terminará nunca teniendo un impacto directo en el DOM y que se utilizara para manejar datos privados o metadatos.$
: un elemento es prefijado con$
para simbolizar que está presente en el DOM, es decir, que es un elemento el cual está montado en la estructura HTML de la aplicación.v
: un objecto/diccionario es prefijado conv
cuando es un vNode, es decir, la representación en el vDOM de un elemento del DOM.
- Clonar o descargar el repositorio https://github.com/GerardRodes/Users-DB.
- Dentro de la carpeta descargada, asegurándote de tener Node.js y npm instalados, ejecutar el comando:
npm install
, se instalarán todas las dependencias necesarias. - Introduciendo el comando
npm start
la aplicación se iniciara en http://localhost:9000.- Si al introducir
npm start
salta algún error del tipocouldn’t resolve
, instalar el paquete que se menciona connpm install nombreDelPaquete
, o poneros en contacto conmigo para poder resolverlo. npm start
inicia tanto el servidor json-server como la aplicación, al estar un rato funcionando el servidor puede dejar de response, creo que se puede deber a estar ejecutando ambos comandos desde la misma instancia de la consola, se soluciona parando el proceso conCtrl+C
y volviendo a ejecutarnpm start
. Si el error persiste, probar a ejecutar primero el servidor connpm run server
, y luego, en otra instancia de la consola aparte, iniciar la aplicación connpm run dev
.
- Si al introducir
- Para ejecutar los tests, ejecutar el comando
npm test
.
El Virtual DOM, o vDOM, es una representación virtual (en este caso, en lenguaje de JS), de la estructura de elementos presentes en el DOM (Document Object Model). Para realizar tal representación se entiende cada elemento del DOM como un diccionario (u objeto, mencionado en el código como vNode), el cual está compuesto de tres atributos básicos:
- El nombre del tag que lo representa (div, span, ul, li, …)
- Las propiedades que lo conforman (desde propiedades del dom como id, class, etc hasta propiedades para el funcionamiento interno del vDOM)
- Sus hijos, es decir, los elementos los cuales están alojados dentro del elemento, en relación directa.
Cada uno de los web components presentes en la web contienen una función render
, la cual retorna una serie de objetos (mencionados como vNodes), en estructura de árbol. Al estar en estructura de árbol se obtiene un solo objeto, formado por múltiples vNodes, el cual representa toda la estructura de la aplicación. Luego estos objetos se parsean
para generar los objetos que representan y mantenerse enlazados y actualizados.
Para generar un vNode puede hacerse escribiendo un objeto tal cual, con los atributos necesarios, pero hemos de representar todos nuestros elementos HTML con esta sintaxis, por lo que puede hacerse pesado y engorroso. Para facilitar el trabajo podríamos programar una función, a la cual, pasándole unos cuantos parámetros nos generara el vNode correspondiente, por convención, esta función es llamada h
(HyperScript), mi implementación de h
se encuentra en src/vdom/h.js
.
Aunque h
ha facilitado en cierta medida la sintaxis, existe algo más de sintactic sugar
, llamado JSX. JSX nos permite escribir nuestros elementos HTML con una sintaxis XML que nos resultara más familiar, luego, el módulo babel-plugin-transform-react-jsx
se encarga de transformar este XML en múltiples llamadas a nuestra función h
. La asignación de mi propia función h
está configurada en .babelrc
.
Todos estos vNodes generados por la función h
, que conforman nuestro vDOM, necesitan ser parseados
para generar a partir de ellos un DOM real. Para ello tienen que montarse.
Mounting es el proceso mediante el cual a partir de un vNode se genera el elemento que representa y se añade al DOM, los procesos de mounting de mi implementación del vDOM se efectúan en src/vdom/mounting.js
. Cuando un elemento es montado, se vinculan, de manera ciclica, todas sus representaciones, es decir, el vNode, la instancia (si es un componente) y el elemento real en el DOM.
Una vez representado nuestro vDOM en el DOM real, nuestros componentes comenzaran a mutar de estados y propiedades, por lo que la finalidad del proceso de updating, es la de mantener toda esta información actualizada y representada correctamente, tanto en el DOM como en el vDOM.
Los procesos de updating son gestionados desde src/vdom/updating.js
.
Como he explicado anteriormente, para representar los elementos del DOM en el vDOM, se hace a partir de vNodes, aun así, no todos los elementos son iguales, por lo que se requiere por lo menos, de 3 tipos distintos de vNodes:
vNode que representa un text node, el cual es creado con un document.createTextNode
. Este vNode está conformado únicamente por una string, o número, los cuales son transformados a texto.
Este vNode representa un elemento normal del DOM como podría ser un div, input, form, o cualquier otro. Este vNode está conformado por el nombre del tag, sus propiedades, y sus hijos.
Este vNode representa un web component, el cual contendrá la lógica de nuestra aplicación. Está formado por la clase para generar instancias suyas y todos sus métodos y atributos (como la función render
o el atributo state
). En una primera instancia un vComponent no contiene hijos, ya que estos son definidos una vez se ejecuta la función render
que determina la estructura del vComponent.
Para generar un vComponent cuento con la clase Component
definida en src/vdom/Component.js
, la cual otorga métodos como setState
, update
o los life cicle events
.
- https://en.wikipedia.org/wiki/Hypertext
- https://medium.com/@deathmood/how-to-write-your-own-virtual-dom-ee74acc13060
- https://medium.com/@calebmer/write-your-own-react-js-776dbef98b8
- https://swennemans.gitbooks.io/building-your-own-react-js/content/01-basics/01-virtual-dom.html
- https://blog.javascripting.com/2016/10/05/building-your-own-react-clone-in-five-easy-steps/
- https://medium.com/@rajaraodv/the-inner-workings-of-virtual-dom-666ee7ad47cf