Lit-HTML

  • Por
Qué es Lit-HTML y cómo usar esta librería para creación de templates HTML en Javascript, capaz de ofrecer un altísimo rendimiento.

Polymer 3.0 está a punto de salir. En el momento de escribir estas líneas ya han presentado varias "Preview Releases" y quizás cuando leas este texto ya dispongamos de una primera release final. Seguro que como yo hay muchos otros desarrolladores ansiosos por comenzar a trabajar con esta librería basada en Web Components, así que para calentar motores hoy vamos a hablar de Lit-HTML.

Lit-HTML es una librería Javascript creada por el equipo de Polymer. Sin embargo, es una librería autónoma, que se puede usar en cualquier sitio, incluso fuera del marco del desarrollo con Polymer.

La librería se ha desarrollado para satisfacer las demandas de un sistema de templates HTML, capaz de aportar un gran rendimiento, funcionando del lado de Javascript con ES6 Modules. Estamos actualmente en una fase temprana de desarrollo, pero ya resulta muy prometedora, por diversos motivos que vamos a explicar en este artículo.

Si sigues leyendo te voy a ofrecer una introducción a Lit-HTML, una comparación con VDOM y finalmente una descripción de los primeros pasos para usar Lit-HTML, directamente con Javascript, sin usar Polymer.

Por qué nace Lit-HTML

Para entender el nacimiento de Lit-HTML tenemos que mirar hacia Polymer 3.0. La novedad principal con respecto a Polymer 2 es el cambio de los HTML Imports a los ES6 imports. Este detalle hace que varias cosas hayan cambiado en Polymer, ya que ahora el marco de desarrollo de un componente no es un archivo HTML, sino un archivo Javascript.

Por tanto, había que buscar una manera amistosa con el desarrollador, además de potente y versátil, para conseguir definir templates a partir de código Javascript. Otras librerías habían hecho un gran trabajo en este sentido, como ReactJS con su "Virtual DOM" (VDOM), pero todavía había margen de mejora.

En las primeras previews de Polymer 3 se escribía el template con un ES6 Template String. Pero esa primera aproximación no aportaba la flexibilidad y rendimiento que el equipo de Polymer deseaba, por lo que idearon Lit-HTML, un nuevo sistema de templates HTML, amistoso, ligero y potente, que se definen desde Javascript.

Claves de Lit-HTML

Los sistemas de templates se crean para ofrecer al desarrollador una manera de escribir código de las vistas, en HTML, de manera que sea fácilmente mantenible. Además, para establecer un puente entre los datos que están en Javascript y la presentación que está en HTML.

Sin embargo, los requisitos a satisfacer para un sistema de templates son a veces difíciles de combinar. Si queremos ofrecer una amigable experiencia de desarrollo muchas veces irá en detrimento del rendimiento del sistema de templates. Es decir, a menudo ocurre que un código ideal para el desarrollador, se traduce en la necesidad de mayor procesamiento para Javascript. Encontrar un compromiso entre estas dos partes es lo que se ha procurado al crear Lit-HTML.

Los objetivos sobre los que se ha construido Lit-HTML, son los siguientes:

  • Ofrecer una experiencia amistosa y versátil para el desarrollador.
  • Ofrecer un elevado rendimiento, al arrancar los templates, pero sobre todo al actualizar el contenido de los templates.
  • Facilitar la extensibilidad del sistema de templates, de modo que otros desarrolladores puedan extenderlo fácilmente aportando nueva funcionalidad.

Además, debido al cambio relatado de los templates de Polymer, que han pasado de HTML al lado de Javascript, se necesitaba cubrir esos requisitos del lado de Javascript y no del lado del HTML como teníamos anteriormente.

La librería desarrollada para cubrir esta necesidad ha conseguido aunar de la mejor manera posible los requisitos iniciales.

  • Permite escribir HTML dentro de código Javascript, de una manera amistosa para el desarrollador. A la vez, es mucho más poderosa que un simple template string de ES6.
  • Genera rápido los templates, básicamente porque aprovecha en su mayor parte código nativo de Javascript ES6 y dispone además de un sistema de cacheo de templates. Gracias a ese sistema de cacheo, si se usa el mismo template varias veces (algo bastante común), el proceso de generación del template reaprovecha el procesamiento anterior.
  • Se actualiza muy rápido cuando hay datos que cambian dentro del template, incluso más rápido que el VDOM de React (Luego haremos una pequeña comparación de ambos sistemas)
  • Es muy pequeño. Ocupa menos de 2KB.
  • Es fácil de usar y extensible para los desarrolladores, que pueden crear nueva funcionalidad encima de la librería para satisfacer futura demanda.

Comparativa de Lit-HTML y VDOM

Una de las librerías para trabajo con vistas más representativas hasta el momento es la implementada en ReactJS, llamada VDOM o "Virtual Dom". Desde que apareció ha cautivado a la comunidad debido a su alto rendimiento a la hora de manipular el DOM de la página.

El modo de funcionamiento de VDOM es de sobra conocido: Existe un DOM virtual creado en memoria. Cada vez que los datos asociados a un template cambia, se realiza una actualización del DOM virtual, que se produce extraordinariamente rápido. Luego, se compara el DOM virtual contra el DOM real del navegador y se actualiza simplemente aquellos nodos que han sufrido transformaciones. Actualizar el DOM del navegador es más costoso en tiempo de procesamiento, por lo que el VDOM consigue optimizar las actualizaciones en el template gracias a que realmente sólo toca el DOM real en los lugares donde es estrictamente necesario.

Sin embargo, esta aproximación todavía puede mejorarse en varios sentidos:

  1. En un template existen partes estáticas y partes dinámicas. Las partes estáticas no se actualizan nunca y las dinámicas, que básicamente son los datos que tienes que volcar en el template, sí pueden recibir cambios. Es inútil comprobar cambios en todo el DOM del template, cuando realmente los cambios sólo se pueden llegar a producir en determinados y concretos puntos.
  2. Habría que ver hasta qué punto es realmente necesario mantener una copia del DOM, en un "Virtual DOM". Si se consigue realizar el mismo trabajo sin esa copia, estaremos rebajando la cantidad de memoria y procesamiento.

Ahí es donde Lit-HTML ha podido optimizar VDOM. No crea un DOM virtual y separa el template en sus partes estáticas y las partes dinámicas. Cuando surgen cambios en los datos solamente necesita comprobar las partes dinámicas del template, actualizando aquellos nodos que realmente son necesarios.

En resumen, mientras que en VDOM el coste de procesamiento (la complejidad del algoritmo) crece a medida que aumenta el número de nodos de un template, en Lit-HTML crece solamente en función de los puntos del template donde se están interpolando valores. Aparte de ocupar menos espacio en KB que VDOM y gastar menos memoria, en la mayoría de los casos, el algoritmo de Lit-HTML es más optimizado, porque tiene que hacer un menor número de comprobaciones por template actualizado.

Nota: El equipo de Google ha publicado un benchmark en el que comparan el motor de plantillas Lit-HTML contra el implementado con VDOM de React, incluso el de Preact y el de Inferno, Angular, etc. En las métricas obtenidas hasta el momento Lit-HTML ha resultado ganador.

La imagen que vemos a continuación trata de explicar este punto de la complejidad algorítmica de varios sistemas de trabajo con vistas (está extraída de una charla sobre Lit-HTML de Justin Fagnani @justinfagnani en el Chrome Dev Submmit 2017):

En el diagrama anterior tenemos los nodos estáticos marcados con color azul y los dinámicos en verde, donde realmente hay cosas que pueden cambiar a medida que los datos del template se actualizan. Sin embargo, a la hora de producirse cambios en un template, no todos los datos cambian, por lo que es posible que no todos los puntos dinámicos se tengan que actualizar verdaderamente. La "A" representada en este diagrama indica que este nodo de datos es el que ha cambiado.

Tenemos tres alternativas de algoritmos representados:

  • VDOM: donde el coste por actualización del template (complejidad del algoritmo) depende del número de nodos del template, tanto los estáticos como los dinámicos.
  • Lit-HTML: donde el coste de actualización del template depende del número de nodos dinámicos.
  • Polymer: donde el coste de actualización depende del número de nodos que han cambiado.
Nota: A la vista de la anterior imagen, de quedarnos con algún algoritmo, quizás perferiríamos el de Polymer. Pero realmente hay que tener en cuenta dos parámetros para el rendimiento: 1) El coste por número de nodos y 2) El coste que te lleva cada nodo. ReactJS, Preact o Inferno con VDOM son muy buenos porque han podido optimizar el coste por nodo, por procesarse en memoria, pero sin embargo están obligados a comparar un gran número de nodos. Polymer tiene un coste por nodo alto, pero en cambio realiza menos actualizaciones de nodos, ya que solo comprueba los nodos que realmente se han actualizados. La ventaja de Lit-HTML es combinar lo mejor de ambas alternativas. Además, usa comportamientos nativos de Javascript en su mayor medida y gracias a ello su peso es menor y la velocidad es mucho mayor. Lit-HTML escala en función de los puntos dinámicos del template, que podrían ser menos puntos que los que han cambiado verdaderamente, pero al producirse el procesamiento más rápido, consigue mayorb rendimiento que los otros.

Recursos nativos en los que se apoya Lit-HTML

Como viene siendo costumbre en Polymer, los ingenieros de Google han producido un algoritmo que usa las capacidades existentes de manera nativa en los navegadores. Lo resumimos a continuación, aunque de todo este proceso no nos tenemos que preocupar como desarrolladores, ya que es algo que realiza Lit-HTML por debajo. Sin embargo, sí que es interesante de conocer para que podamos entender mejor cómo se usará este nueva librería.

Etiqueta nativa Template

A la hora de construir Lit-HTML se han basado en la etiqueta template existente en todos los navegadores de la actualidad.

Lo que hacen es construir una etiqueta template, un template HTML nativo. En ese template memorizan los huecos donde se insertan los valores que vienen desde Javascript. Al cambiar los datos que se vuelcan al template, saben en qué puntos deben colocarlos, con lo que las operaciones se realizan muy rápido.

Además, el coste de creación de un template Lit-HTML se realiza una vez por template. Si más adelante se usa ese template, porque se repita un determinado bloque varias veces, se reutiliza el template tag generado la primera vez.

Tagged Template String Literals

Son de sobra conocidos los Template Strings de Javascript ES6. Sin embargo Lit-HTML va un paso más allá para sacar partido de los "Tagged Template String Literals".

Son simplemente literales de string en los que tenemos varias capacidades extra:

  • Somos capaces de escribir las cadenas en varias líneas de código.
  • Podemos usar expresiones Javascript en ellas ${} (estas expresiones son las que los propios template strings ya nos implementan)
  • Se les aplica una etiqueta, como un nombre, que es una función que procesa los literales de template string y sus expresiones. Además es capaz de devolver cualquier cosa, no necesariamente una cadena, como un objeto.

Un literal de Tagged Template String sería como este:

html`
  <div>
    <h1>${titulo}</h1>
    <p>${descripcion}</p>
  </div>
`

La palabra "html" del inicio es el tag (la etiqueta), una función que procesará ese literal de cadena y nos devolverá el objeto template.

Usar Lit-HTML

Ahora vamos a dar unas primeras notas sobre cómo poner las manos encima de Lit-HTML y comenzar a usarlo en el marco de Javascript, sin aplicar ningún framework.

Primero debemos instalar Lit-HTML en el proyecto.

npm install lit-html

Luego podremos usar la librería. Pero ten en cuenta que está desarrollada en ES6, por lo que tendremos que variar un poco la manera habitual con la que la usaremos en este ejemplo, apoyándonos en los ES6 Modules.

Para este ejemplo voy a tener dos archivos:

index.html

Con el código de una página básica, donde voy a volcar el contenido de los templates generados. Realmente es un archivo HTML prácticamente vacío, pero creo que es bueno ver el código porque hace uso de un script que se carga como módulo de ES6, que quizás sea desconocido por algunas personas.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Lit-html Demo</title>
  <script src="./lit-html-demo.js" type="module"></script>
</head>
<body>
  
  <div id="ej1"></div>
  <div id="ej2"></div>
  <div id="ej3"></div>
</body>
</html>

El script de Javascript donde realizamos el procesamiento para el uso de Lit-HTML lo estamos trayendo como un módulo, para poder realizar imports de ES6. Observarás el atributo type="module".

Nota: por si no los conoces, te recomendamos un artículo de DesarrolloWeb donde puedes aprender más sobre los módulos ES6.

Luego verás en el index.html tres contenedores donde vamos a volcar el contenido de las templates una vez asignados valores a sus expresiones. Fíjate que tienen identificadores definidos, pues vamos a referirnos a ellos desde el Javascript.

lit-html-demo.js

La gracia de este ejercicio la tenemos en el Javascript que hace uso de Lit-HTML. Aquí es donde vamos a generar un par de templates y vamos a producir un HTML a partir de la carga de datos para alimentar sus expresiones.

El primer paso consiste en obtener las cosas que necesitamos para trabajar con Lit-HTML, con un import de ES6.

import {html, render} from "./node_modules/lit-html/lit-html.js";

Observarás que nos estamos trayendo dos declaraciones:

html:

Es el tag para los "tagged template string". Es, como dijimos antes una función que se aplicará a la template string que enseguida vamos a crear.

render:

Es una función Javascript que sirve para volcar el HTML generado con los templates dentro de la página. Render recibirá dos parámetros.

  • El templateResult, que es el resultado de la aplicación de un juego de datos en una plantilla.
  • El nodo del DOM donde tenemos que volcar, como contenido, el HTML generado.

Construir un templateResult

Podemos construir un template usando el tagged template string, al que le tenemos que aplicar los datos para poblar sus expresiones.

const sitioWeb = 'DesarrolloWeb.com';
const objetoTemplateResult = html`<h1>Me gusta ${sitioWeb}</h1>`;

En las dos líneas anteriores, primero creamos una variable con el dato que queremos asignar dentro del template. En la segunda línea creamos el objeto "templateResult" procesando el tagged template string, en el que verás que hemos colocado una expresión donde tiene que aparecer el dato "sitioWeb".

Sin embargo, este código será más versátil si creamos una función que reciba el dato que se desea volcar el el template string. Invocando luego a esa función con un resultado cada vez que se pretenda generar el templateResult.

const miTemplate = (sitioWeb) => html`<h1>Me gusta ${sitioWeb}</h1>`;
const objetoTemplateResult = miTemplate('DesarrolloWeb.com')

Renderizar un template

Ahora nos falta renderizar el template en algún lugar de la página. Para ello usamos la función render() de Lit-HTML, con el templateResult anterior y algún nodo donde deba volcarse el HTML.

render(objetoTemplateResult, document.body);

Esto producirá la escritura de "<h1>Me gusta DesarrolloWeb.com</h1>" como cuerpo de la página. Borrará todo lo que hay en el body y lo sustituirá por el HTML generado.

Entendiendo todos los pasos anteriores debrería de quedar más o menos claro el código siguiente, que pone todo el conjunto de acciones en funcionamiento para usar Lit-HTML.

import {html, render} from "./node_modules/lit-html/lit-html.js";

const miTemplate = (sitioWeb) => html`<h1>Me gusta ${sitioWeb}</h1>`; 

render(miTemplate('DesarrolloWeb.com'), document.getElementById('ej1'));
render(miTemplate('EscuelaIT'), document.getElementById('ej2'));

Hemos volcado los dos bloques de HTML generado en dos elementos de la página, los que tenían los identificadores "ej1" y "ej2". Por eso el resultado que veríamos es algo como este:

Recorrer arrays en un template

Lo que acabamos de ver es simplemente el inicio de las funcionalidades que hay detrás de esta librería. Ahora vamos a ver cómo de versátil puede ser el sistema de expresiones de los template strings para mostrar código que viene desde un array.

Este ejemplo, aunque todavía muy básico, es muy interesante, porque nos demuestra que los templates de Lit-HTML se pueden anidar, colocando nuevos templates dentro de expresiones.

De hecho, una de las claves de la potencia de Lit-HTML es que usa expresiones Javascript nativas. Es decir, en los bloques de las template strings, ${ expresion }, se puede colocar cualquier código Javascript que realice cualquier cosa que se nos ocurra.

const miSegundoTemplate = (intereses) => {
 return html`
   <h2>Mis intereses son:</h2>
   <ul>
     ${intereses.map((interes)=> html`<li>${interes}</li>`)}
   </ul>
 `;
}

En este segundo template lo que tenemos es que la variable "intereses" será un array. Por tanto lo podremos recorrer dentro de la expresión del template string, produciendo para cada ítem un nuevo template con HTML que se repetirá tantas veces como casillas en el array.

Podremos dar uso a este template y volcarlo en cualquier nodo del documento.

render(miSegundoTemplate(['Programación', 'Juegos', 'Viajes']), document.getElementById('ej3'));

Con esto generaríamos una lista que se vería como esto:

Conclusión

Lo que hemos aprendido es sólo el inicio de la funcionalidad que hay detrás de Lit-HTML. Es todavía una librería experimental que se está acabando de perfilar en estos momentos para formar parte de Polymer, aunque podría ser usada en cualquier ámbito del desarrollo Frontend.

Hemos dejado de lado mucha información interesante, que da nuevas claves de lo versátil y avanzado de este sistema de templates en Javascript y las muchas cosas que se puede hacer con él para llegar mucho más lejos.

Polymer 3 usará Lit-HTML, pero le pone por encima nuevas extensiones (algo factible de conseguir ya que el propio Lit-HTML se ha construido de manera que pueda ser extensible) y conseguirá que trabajemos con este sistema de templating sin que tengamos que preocuparnos por mucha de su complejidad, adaptándolo más aún a la experiencia amigable de desarrollo que nos tiene acostumbrados Polymer.

En futuros artículos explicaremos otras parcelas interesantes de Lit-HTML y podremos tener nuevos flashes de las cosas que están por llegar y que pondrán sin duda a Polymer aún más en la mira de todos aquellos desarrolladores preocupados por el rendimiento de sus aplicaciones y el uso de las posibilidades nativas de la plataforma web.

Autor

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

Compartir