Hamburger Icon
Así de fácil es crear Web Components en JavaScript

Así de fácil es crear Web Components en JavaScript

Los Web Components son un conjunto de tecnologías web que permiten la creación de componentes reutilizables y personalizables para construir aplicaciones web.

Conceptos clave

Los tres conceptos clave que debes conocer son estos:

  • Custom Elements: este estándar permite la creación de elementos HTML personalizados. Con Custom Elements, puedes definir tus propios elementos HTML con su propio comportamiento y estilo. Puedes crear elementos personalizados como <my-component> y luego utilizarlos en tu código HTML como cualquier otro elemento HTML nativo.
  • Shadow DOM: proporciona encapsulación al componente. Permite crear un árbol DOM independiente y aislado dentro del elemento personalizado. Esto significa que los estilos, los eventos y la estructura del componente no afectarán ni serán afectados por el resto del documento. Cada instancia de un componente tendrá su propio Shadow DOM. Hace un tiempo escribí un post sobre qué es el DOM, por si quieres hacer un repaso.
  • HTML Templates: son elementos <template> que te permiten definir fragmentos de código HTML que no se renderizan directamente en la página. Puedes utilizar estos templates para crear la estructura inicial del componente y luego clonarlos para crear instancias múltiples del mismo.

Cuando se combinan estos tres estándares, se puede crear un componente web reutilizable y encapsulado. Los Web Components se pueden utilizar en cualquier proyecto web y se integran bien con los frameworks existentes.

Pasos a seguir para crear Web Components

Para crear Web Components normalmente se siguen siempre los mismos pasos.

Primero, defines tu componente personalizado utilizando la API de Custom Elements. Esto implica crear una clase JavaScript que extienda la clase base HTMLElement y definir el comportamiento del componente. Puedes revisar este post sobre el this en JavaScript y sus referencias para entender mejor las clases en este lenguaje.

Dentro de la clase de tu componente, puedes utilizar el Shadow DOM para encapsular el estilo y la estructura del componente. También puedes utilizar HTML Templates para definir la estructura inicial del componente.

Luego, se registra el componente utilizando el método customElements.define para asociar el nombre de tu componente personalizado con la clase que lo implementa.

Finalmente, utilizas el componente personalizado en tu código HTML como cualquier otro elemento HTML nativo.

Ejemplo de Web Component

Vamos a ver un ejemplo de un componente que muestre el precio actual de Bitcoin, te dejo algunos comentarios en el código y una explicación luego:

<!DOCTYPE html>
<html>
  <head>
    <title>Componente precio Bitcoin</title>
    <script>
      // clase que hereda de HTMLElement que define el componente
      class BitcoinPrice extends HTMLElement {
        // en el constructor de la clase usamos attachShadow para asociar el shadow DOM
        // modo open: podemos acceder con JavaScript desde fuera de la raíz que definamos
        // modo closed: no podremos acceder desde fuera del componente
        constructor() {
          super()
          this.attachShadow({ mode: "open" })
        }

        // método especial del ciclo de vida del componente web
        // se invoca cuando el componente se añade al DOM
        // primero queremos renderizar el contenido y luego hacer la llamada a la API
        connectedCallback() {
          this.render()
          this.loadPrice()
        }

        // definimos y encapsulamos el estilo y la estructura del componente (Shadow DOM)
        render() {
          this.shadowRoot.innerHTML = `
          <style>
            :host {
              display: inline-block;
            }
          </style>
          <span id="price"></span>
        `
        }

        // método que obtiene el precio de BTC
        loadPrice() {
          fetch(
            "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd"
          )
            .then((response) => response.json())
            .then((data) => {
              const priceElement = this.shadowRoot.querySelector("#price")
              const price = data.bitcoin.usd
              priceElement.textContent = `Price: $${price}`
            })
            .catch((error) => {
              console.error("Error loading Bitcoin price:", error)
            })
        }
      }

      // se asocia la clase con el nombre del componente personalizado
      customElements.define("bitcoin-price", BitcoinPrice)
    </script>
  </head>
  <body>
    <!-- usando el componente -->
    <h1>Precio Bitcoin:</h1>
    <bitcoin-price></bitcoin-price>
  </body>
</html>

En este código, creamos un nuevo elemento personalizado llamado <bitcoin-price>. El componente utiliza el Shadow DOM para encapsular su estilo y estructura. Al cargar el componente, se renderiza un elemento <span> vacío dentro del Shadow DOM.

Luego, en el método loadPrice(), realizamos una llamada a la API de CoinGecko para obtener el precio actual de Bitcoin en USD. Una vez que se recibe la respuesta de la API, actualizamos el contenido del elemento <span> con el precio obtenido.

Por último, registramos el componente utilizando customElements.define() para asociar el nombre del componente personalizado bitcoin-price con la clase BitcoinPrice que lo implementa.

Cliclo de vida del componente

En este ejemplo se ha usado el método especial connectedCallback() del ciclo de vida del componente, pero existen otros:

  • connectedCallback: invocado cada vez que el componente es añadido o movido.
  • disconnectedCallback: invocado al quitar el componente del DOM.
  • adoptedCallback: invocado cuando se mueve el componente a un nuevo documento.
  • attributeChangedCallback: invocado cada vez que se añade, quita o modifica un atributo en el componente personalizado.

Con estos métodos obtenemos un control completo en todo momento sobre lo que sucede con los componentes web.

Conclusión

Con esto tienes de sobra para empezar a crear tus componentes web y dotarlos de prácticamente cualquier funcionalidad.

Si quieres ampliar conocimientos, puedes consultar esta guía más completa, aunque todavía está en beta en el momento de redactar este post.

Otra fuente bastante útil es la guía de MDN sobre componentes web.

Los Web Components son una poderosa tecnología web que permite crear componentes reutilizables y personalizables para construir aplicaciones web. Al combinar los estándares de Custom Elements, Shadow DOM y HTML Templates, los Web Components ofrecen los siguientes beneficios:

  • Reutilización de código: los Web Components permiten encapsular la lógica y el estilo de un componente en un solo lugar, lo que facilita la reutilización en diferentes partes de un proyecto o incluso en diferentes proyectos.
  • Encapsulación: el Shadow DOM proporciona un alcance de estilo y estructura a nivel de componente, lo que significa que los estilos y elementos internos de un componente no se ven afectados por el resto del documento, y viceversa. Esto evita conflictos y mejora la modularidad del código.
  • Interoperabilidad: son independientes de frameworks y bibliotecas específicos. Pueden utilizarse en cualquier proyecto web, integrándose sin problemas con otros frameworks como Angular, React o Vue.js.
  • Mantenimiento simplificado: al tener componentes autocontenidos y reutilizables, el mantenimiento del código se vuelve más sencillo. Los cambios realizados en un Web Component se propagan automáticamente a todas las instancias utilizadas en la aplicación.
  • Amplia compatibilidad: son compatibles con la mayoría de los navegadores modernos sin necesidad de dependencias adicionales o polyfills. Esto garantiza que los componentes funcionen de manera consistente en diferentes entornos.

En resumen, los Web Components ofrecen una solución potente para crear componentes web modulares, reutilizables y encapsulados. Al utilizarlos, puedes mejorar la estructura de tu código, la legibilidad, el mantenimiento y la interoperabilidad, facilitando el desarrollo de aplicaciones web robustas y escalables.