DÍA 15 / 2018

Web Components: el futuro es ahora... ¿o no?

Era el año 2011 y Alex Russell maravillaba a muchos con la charla Web Components and Model Driven Views en la conferencia fronteers de Amsterdam. Allí explicó una serie de nuevas especificaciones que traían la idea, casi utópica por aquel entonces, de poder crear elementos totalmente personalizables a través de nuevas etiquetas HTML. En estos días hemos conocido que GitHub ha eliminado su dependencia de jQuery y ha apostado enteramente por Javascript puro, un puñado de polyfills y el uso de Web Components. ¿Deberíamos hacer todos lo mismo? ¿Son lo que nos prometieron hace más de un lustro?¿Quieres saber qué son, cómo crear uno y más detalles? Pues sigue leyendo que te lo cuento.


doc-framework-frontend

Vamos a deshacernos en el ácido del recuerdo. Recordemos 2011. Qué jóvenes y hermosos eramos 🧒👦. Paul Irish todavía no había escrito su mítico artículo box-sizing: border-box FTW!, la comunidad estaba volcada en usar HTML5 Boilerplate para montar sus proyectos web, porque usaba jQuery desde un CDN, y jQuery era Dios. Google seguía trabajando en el plugin Chrome Frame para llevar las últimas tecnologías y su motor Javascript a Internet Explorer (murió en 2013) y todos nos lo pasábamos pipa con Internet Explorer 6 y sus hacks usando como editor Notepad++, Gedit, Kate o… Vim, entre otros. Eso sí, todos estábamos alucinando con el iPhone 4s 📱.

Espero que os hayáis situado porque en esas fechas, como introducía en el artículo, Alex Russell ya estaba hablando de Web Components. ¿Te haces a la idea? Lo fugaz que parece, en ocasiones, el mundo del frontend, la programación y la tecnología. Y, sin embargo, cuando hablamos de especificaciones HTML y de matar el soporte para la versión de Internet Explorer que no deja de darnos la lata, lo lento que parece que transcurre el tiempo. Ciertamente inquietante 🛸.

¿Y de qué hablaba el bueno de Alex Russell por aquél 2011? Vamos a repasarlo.

Partiendo de la idea idílica 💡

Los Web Components partían con una serie de ideas idílicas para poder crear nuevos elementos encapsulados en tu navegador de forma que pudiéramos jubilar las librerías que estábamos usando. Estas ideas eran:

  • Disponible en todos los sitios sin riesgo a su adopción.
  • Estándar recomendado por la W3C sin riesgo a acoplarse a una tecnología.
  • Alto rendimiento sin necesidad de sacrificar tiempo de ejecución.
  • Mantener las etiquetas semánticas y declarativas.

Pero había algo todavía más interesante detrás de todo esto. Si se conseguía un estandard que te permitiese crear nuevos elementos HTML e incluso extender los actuales se resolvería uno de los grandes problemas de la web. ¿Y qué problema es ese? Pues la falta de adopción de los nuevos estándares o etiquetas de forma que la evolución de la web no se vea limitada por la siguiente versión del navegador. ¿Os suena? Algo parecido se quiere conseguir con Houdini CSS, salvando las distancias, y la idea es acelerar la adopción de las próximas tecnologías y no dejar los desarrollos estancados.

¿Qué son los Web Components?

Antes de nada vamos a ver qué son exactamente, a día de hoy, los Web Components. Mucha gente cree que, simplemente, es la tecnología que permite crear nuevas etiquetas HTML y no es así. En realidad los Web Components son una serie de tecnologías distintas que pueden ser utilizadas de forma independiente.

Imagen relacionada
  • Custom Elements: Describe el método para permitir a los desarrolladores definir y usar nuevos tipos de elementos del DOM en un documento HTML.
  • HTML Templates: Te permite definir fragmentos de HTML que pueden ser clonados o insertados en un documento.
  • Shadow DOM: Esta especificación determina cómo tener múltiples árboles de elementos en una sola jerarquía, cómo estos árboles interactúan entre ellos siendo independientes y permitiendo una mejor composición.
  • ES Modules: Es el estándar de ECMAScript para importar módulos Javascript. Permite tanto la carga síncrona como asíncrona de los mismos, los puede analizar estáticamente y, por lo tanto, nos ofrecerá una carga más óptima de nuestras dependencias.
  • HTML Imports: Era una forma de importar documentos HTML en otros documentos HTML. Digo era ya que es una característica abandonada. Hay un interesante debate sobre empezar de cero una nueva característica llamada HTML Modules, siguiendo la filosofía de los ES Modules de Javascript.

Crea tu primer Web Component

Imaginemos que queremos crear un elemento HTML que nos permite introducir, siempre que queramos, un placeholder de un lindo gatito 😸. Para ello, vamos a utilizar el servicio placekitten.com que es gratuito y muestra una imagen diferente cada vez que usamos una de sus direcciones. El elemento que crearemos para ello es el siguiente:

<placeholder-kitten />
A la hora de crear un Custom Element, hay que tener en cuenta que el nombre debe ser de dos palabras separadas por un guión. La especificación lo demanda ya que de esta forma no podréis poner nombres de una sola palabra que puedan colisionar con un nuevo elemento HTML en el futuro.

Creando el Custom Element

Para ello, debemos crear un archivo index.js, donde crearemos y definiremos nuestro primer Custom Element. Para ello, debemos crear una clase que extienda de HTMLElement.

class PlaceholderKitten extends HTMLElement {}

Ahora, tendremos que añadir el método connectedCallback. Es un método que viene heredado de HTMLElement y que se dispara una vez nuestro elemento se ha añadido en el DOM. Ahí ya tendremos acceso al DOM para poder crear el elemento.

class PlaceholderKitten extends HTMLElement {
    connectedCallback () {
        this.innerHTML = "<img src='https://placekitten.com/200/300' />"
    }
}

Ya tenemos la implementación de nuestro Custom Element. Ahora, solo falta que conectemos que la etiqueta <placeholder-kitten /> se refiere a la clase PlaceholderKitten y que nuestro HTML sepa qué tiene que renderizar. Para conseguirlo, sólo tenemos que añadir la siguiente línea después de la clase.

window.customElements.define('placeholder-kitten', PlaceholderKitten)

Una vez hecho esto, ya sólo nos queda cargar el archivo Javascript en nuestro archivo index.html y usar el elemento que hemos creado.

<script src="index.js"></script>
<placeholder-kitten />

Aquí tenéis el resultado final en Code Sandbox para que podáis editarlo:

class PlaceholderKitten extends HTMLElement {
  connectedCallback() {
    this.innerHTML = "<img src='https://placekitten.com/200/300' />"
  }
}
window.customElements.define('placeholder-kitten', PlaceholderKitten)

Edit Custom Element with wrong styles

Usando el Shadow DOM

Nuestro Custom Element funciona perfectamente pero ahora nos gustaría darle algo de estilos. ¿Qué hacemos? Podríamos usar la hoja de estilos global del documento pero entonces nuestro componente no estaría bien encapsulado. Una cosa que podríamos hacer, es cambiar la implementación de nuestro componente. Añadirle unos estilos en línea y además añadirle una clase al elemento:

class PlaceholderKitten extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `
    <style>
    .kitten {
      background: gray;
      border-radius: 3px;
      padding: 6px;
    }
    </style>
    <img class='kitten' src='https://placekitten.com/200/300' />
    `
  }
}

Edit Custom Element with wrong styles

Pero esto tiene diversos problemas. 🙈 Para empezar, esto haría que si tenemos un elemento <p class='kitten'>🐈</p> entonces se estilará con esos mismos estilos ya que aunque los estilos son en línea está afectando a todos los elementos del DOM. Y el otro problema es que cada vez que usemos este elemento, se añadirá un buen cacho de estilos de forma que no es nada óptimo.

¿Y cómo solucionamos esto? Pues… ¡Shadow DOM al rescate 👻!. El Shadow DOM nos va a permitir tener un DOM totalmente independiente donde sus elementos y estilos no afectan a los otros, de esta forma podemos hacer nuestros componentes totalmente aislados y agnósticos de lo que ocurre fuera y, lo mejor, evita cualquier tipo de efecto colateral no deseado.

Para utilizarlo vamos a dejar de usar el método connectedCallback y, en su lugar, vamos a usar el constructor. Como usamos el constructor, primero debemos llamar al método super para inicializar el constructor de la clase que heredamos, HTMLElement. Después le vamos a indicar que este elemento lo vamos a adjuntar a un Shadow DOM en modo abierto (significa que podríamos acceder a este elemento usando Javascript escrito en el mismo contexto de la página). Para acabar, solo tenemos que reutilizar el innerHTML que teníamos antes pero ahora lo insertamos en la raíz del Shadow DOM.

class PlaceholderKitten extends HTMLElement {
  constructor() {
    super()
    this.attachShadow({ mode: 'open' })
    this.shadowRoot.innerHTML = `
    <style>
    .kitten {
      background: gray;
      border-radius: 3px;
      padding: 6px;
    }
    </style>
    <img class='kitten' src='https://placekitten.com/200/300' />
    `
  }
}

window.customElements.define('placeholder-kitten', PlaceholderKitten)

Edit Custom Element with wrong styles

Y, ahora sí, los estilos de nuestro gatito no afecta a otros elementos fuera del elemento. Y ya hemos aprendido muy fácilmente como usar el Shadow DOM en nuestros elementos.

Si observamos el HTML resultante con las herramientas de desarrollo podremos observar como se indica claramente que el elemento no está renderizando simplemente código HTML, si no que tiene un Shadow DOM.

Si tenéis más dudas sobre el Shadow DOM y todas sus posibilidades, que ya os adelanto que son muchas, os recomiendo que no os perdáis este Gist que contiene todo lo que necesitas saber sobre esta característica.

Usando HTML templates

Otra forma de conseguir lo misom que hemos visto anteriormente sería usar HTML templates. Esto era bastante más interesante cuando los HTML Imports parecían una realidad y ahora habrá que usar ES Modules para separar el template de la implementación del elemento. En este caso, deberíamos crear el elemento template y hacer su contenido sea el HTML que, más tarde, vamos a clonar y añadir como hijo al elemento que tenemos en el Shadow DOM.

const template = document.createElement('template')
template.innerHTML = `
<style>
  .kitten {
    background: gray;
    border-radius: 3px;
    padding: 6px;
  }
</style>
<img class='kitten' src='https://placekitten.com/200/300' />
`

class PlaceholderKitten extends HTMLElement {
  constructor() {
    super()
    this.attachShadow({ mode: 'open' })
    this.shadowRoot.appendChild(template.content.cloneNode(true))
  }
}

window.customElements.define('placeholder-kitten', PlaceholderKitten)

Edit Custom Element with Shadow DOM and template

👀 A tener en cuenta

Limitaciones y cosas a tener en cuenta con los ejemplos que he puesto de Code Sandbox.

  • Los ejemplos deberían ser totalmente funcionales en las últimas versiones de los navegadores actuales. Excepto Edge, que todavía necesita polyfills para los Custom Elements y el Shadow DOM.
  • La versión 1 de la especificación de los Custom Elements no es compatible con el estilo de clases de ES5. Eso significa que si transpiláis vuestro código, os puede dar problemas. Hay workarounds sobre esto, pero para simplificar, en mis ejemplos estoy desactivando completamente Babel.
  • Si jugáis con los ejemplos es posible que, cuando hagáis cambios y a causa del Hot Reloading, no se refresque la página y os de un error que el nombre ya está usado. Eso es porque la etiqueta se registra en el window y obviamente sólo se puede tener una etiqueta con el mismo nombre registrada. Dadle al botón actualizar del previsualizador directamente para ver los cambios.

Lo bueno, lo malo y lo feo

bad-ugly

Una vez hemos visto en qué consisten y cómo implementar sus tecnologías de la forma más sencilla posible os voy a dar mi punto de vista sobre sus bondades y las razones por las que esta tecnología, que lleva desde 2011, todavía no ha despegado como se esperaba. 🚀

En este tiempo, desde 2011, hemos visto la superación de jQuery como estándar, la revolución del mundo del frontend como fuente de la verdad para generar páginas (cuando antiguamente eran lenguajes como Java, PHP o .NET) y la irrupción de algunas librerías en el panorama del frontend que ha traído conceptos más allá de los componentes. Después de unos primeros años en los que parecía que Angular iba a ser la preferida, React se ha convertido en el rey 👑 de las librerías de frontend y ahora alternativas como VueJS están atrayendo también muchos desarrolladores. Entre todas las ventajas que ofrecían estas librerías, ¿alguien ha echado realmente de menos los Web Components en todo este tiempo?

Quizás esa falta de interés o necesidad explica por qué la comunidad se ha tomado con más calma el desarrollo y adopción de estas tecnologías de forma masiva.

img
Las contrataciones de React no han dejado de aumentar en los últimos meses

Sea como sea el soporte nativo en navegadores sigue siendo pobre. No hablamos de versiones anteriores de Internet Explorer 11. Hablamos que a la hora de un soporte total sólo Chrome parece estar en verde con soportes parciales, y en ocasiones con bugs, en Firefox y Safari. Es cierto que últimamente se ha dado pasos de gigante en ese aspecto y seguramente pronto veamos más compatibilidad por los navegadores modernos, pero la necesidad de una ristra de polyfills parece que será durante un tiempo una realidad para poder utilizarlos de forma segura.

image-20181012112855002

Tampoco las especificaciones de los Web Components han resultado ser muy estables. De algunos estándares, unos se han deprecado ya, otros todavía tienen algunas cosas que arreglar por falta de acuerdos y existen ya dos versiones de las especificaciones más importantes. Básicamente, durante estos últimos años le ha faltado una estabilidad que le permitiese asentarse.

Por otra parte, otro de los grandes puntos negros, es la falta de un sistema de transmisión de datos, limitando los atributos a simples strings hace que el uso de Web Components queden para elementos demasiado simples como para pagar el coste actual de introducirlo en nuestras aplicaciones. Es una lástima que se perdiese por el camino la intención de estandarizar también el

Lo bueno es que, aunque lento, cada vez vemos más y más components hechos con la tecnología e incluso existe un catálogo de ellos donde podemos usarlos. Creo, sinceramente, que cuando los navegadores por fin abrazan esta tecnología veremos como el uso de Web Components se ve disparado. Va a costar llegar a ese punto, pero una vez que lleguemos allí creo que va a ser imparable. También es verdad que los Web Components no son perfectos pero, ¿qué ha sido perfecto en la web en sus primeros años? ¿es que acaso lo es ahora? En realidad, todo lo contrario, siempre estamos en constante evolución.

Sin embargo los Web Components parte sobre ideas altamente atractivas que nos van a seducir sí o sí tarde o temprano. Tener elementos totalmente personalizados en nuestro navegador, de forma sencilla, sin necesidad de cargar librerías de terceros y, además, totalmente compatible con todos los navegadores. Y todo eso gracias a utilizar la plataforma. ❤️

¿Y ahora…

va a matar los Web Components librerías como React, Vue o Angular? 🔪

Respuesta corta: No. 😅

Respuesta larga: Es posible que veamos, cada vez más, componentes simples hechos con Web Components pero todavía las librerías como React solucionan muchos problemas. De hecho es evidente que el modelo conceptual que tiene detrás librerías como React o Vue es muy superior al que traen los Web Components (DOM virtual, streaming de server side rendering, fuerza patrones de diseño más escalables, todo lo que comporta obtener una programación declarativa…). La pregunta es si necesitas esas características y te compensa su coste de cargar la librería. Además, que ambas tecnologías no son excluyentes.

debería pasar todos mis sites y apps a Web Components? 🤩

Mucha gente se hace esta pregunta e incluso veo en algunas empresas compañeros que están decididos a lanzar por la ventana su actual stack porque ha saltado la noticia que GitHub ha pasado todo a Vainilla Javascript y Web Components.

La respuesta a esta pregunta no es fácil y depende de muchos factores. En el ejemplo de GitHub, partían de un código base muy antiguo con jQuery, que su target es el de usuarios que siempre van a contar con los últimos navegadores (aunque no he encontrado datos de GitHub sí los hay de Jenkins que es una herramienta bastante asociada con la primera) y quizás no necesitan en algunos casos contar con Server Side Rendering, por ejemplo.

img

Además, es cierto que ahora han terminado de moverlo todo pero llevan ya más de 4 años jugando con web components y que, por mucho que he estado trasteando la web, veo que el uso de Web Components es muy selectivo.

Código de GitHub donde podemos ver tres Custom Elements: relative-time, include-fragment y task-lists.

Además, si tienes ya todos tus componentes presentacionales en VueJS, React o cualquier librería de vistas o framework moderno, no te recomendaría dar el salto. De la misma forma, si necesitas soportar navegadores antiguos, prepárate para el festival de polyfills y problemas que te puedes encontrar.

Dicho esto, hay cada vez más webs y proyectos que están empezando a usarlos. Y puede ser tu caso. Sólo ten en cuenta las desventajas y, si lo tienes claro, no lo dudes. 🙌

pero muchos problemas que comentas los soluciona Polymer, no? 🤔

Efectivamente. Una de las opciones para solventar muchos, que no todos, de los problemas o falta de características que tienen los Web Components sería utilizar una librería como Polymer, Stencil, Slim o SkateJS. El uso de polyfills para navegadores antiguos sería todavía necesario junto con la librería que decidas utilizar pero va a facilitar mucho la vida a la hora de trabajar con Web Components. Eso sí, algunos idealistas dirán que la idea principal de los Web Components era precisamente no utilizar librerías 🤷‍♀️ pero hay que reconocer que estas librerías, normalmente, tienen un tamaño bastante menor a otras alternativas gracias a utilizar esta tecnología.

¿El futuro es ahora?

Ahora ya sabes qué son los Web Components, cómo usar sus tecnologías y los retos que presenta de cara al futuro. Creo que vienen muy buenos tiempos para los Web Components y poco a poco vamos a ir viendo un goteo constante de buenas noticias para ellos. Aunque actualmente, por responsabilidad, no me atrevería a introducirlo en los productos en los que trabajo con millones de usuarios, creo que sí es algo que muy pronto va a llegar para quedarse. Espero que este artículo os haya servido para conocer más sobre lo que nos espera y que os anime a aprender más. Ahora te toca a ti decidir si, en tu caso, el futuro de los Web Components es ahora.

Muchas gracias por leerme y seguimos en contacto en Twitter con mi cuenta @midudev donde hablo de frontend y comparto recursos. 👨‍💻