Categorías
Hablo de código y gestión

Aprendiendo Web Components

¿Qué son los Web Components?

Los Web Components son una agrupación de tecnologías web estándar que permiten crear elementos personalizados reutilizables con su propia estructura, estilo y funcionalidad, completamente encapsulados y aislados del resto del código. Esto facilita la creación de componentes modulares, evitando problemas de estilos y comportamientos entre elementos, y permite su uso en diferentes proyectos sin necesidad de depender de frameworks específicos.

Según la documentación de MDN:

«Los Componentes Web son un paquete de diferentes tecnologías que te permiten crear elementos personalizados reutilizables — con su funcionalidad encapsulada apartada del resto del código — y utilizarlos en las aplicaciones web.»

Los Web Components se basan principalmente en tres tecnologías clave:

  1. Custom Elements: Nos permite definir nuestros propios elementos HTML con comportamiento personalizado.
  2. Shadow DOM: Proporciona un encapsulamiento real del DOM, evitando colisiones de estilos y manteniendo el código verdaderamente modular.
  3. HTML Templates: Permite definir estructuras de marcado que se pueden reutilizar y clonar sin necesidad de realizar repintados costosos.

Pasos para crear un Web Component sin frameworks, usando vanilla JS

1. Usando el estándar de Módulos de JavaScript (JS Modules)

Los módulos de JavaScript son una funcionalidad nativa que permite dividir el código en archivos separados, cada uno con su propio ámbito, facilitando la reutilización y mantenimiento del mismo.

Según la documentación de MDN:

«Los programas JavaScript comenzaron siendo bastante pequeños. Hoy en día creamos aplicaciones completas del lado del cliente con mucho JavaScript, y los módulos ayudan a organizar, cargar de forma asíncrona y mantener este código de forma sencilla y escalable.»

Estructura básica de un módulo:

  • Declarar el módulo desde un archivo HTML: <script type="module" src="js/wcicon.js"></script>
  • Declarar el código del módulo directamente en un archivo .js: // Ejemplo: module.js export function saludar() { console.log("Hola desde un módulo!"); } Luego, en otro archivo JS o en el HTML: import { saludar } from './module.js'; saludar(); // "Hola desde un módulo!"

2. Estructura básica de un Web Component

Para empezar, muchos tutoriales se limitan a mostrar un «Hello World» dentro de un componente. En este caso, iremos un paso más allá: imaginemos que necesitamos un componente personalizado que muestre un ícono específico según un atributo. Por ejemplo, un ícono de Twitter:

<wc-icon typeicon="twitter"></wc-icon>

Reglas al crear un Custom Element (Web Component):

  • El nombre de la etiqueta personalizada debe contener un guion (-). Por ejemplo: <wc-icon> cumple con esta regla. Esto evita conflictos con elementos HTML nativos.
  • Puedes añadir tantos atributos como necesites: <wc-icon typeicon="twitter" size="large"></wc-icon>.

Llamada al Web Component como módulo:

En el HTML donde se use el componente, debemos importar el archivo del componente como un módulo ES6:

<script type="module" src="js/wcicon.js"></script>

Estructura básica en JavaScript:

// wcicon.js

class WcIcon extends HTMLElement {
  constructor() {
    super();
    // Definir atributos por defecto
    this.typeicon = "default";
  }

  // Lista de atributos a observar
  static get observedAttributes() {
    return ["typeicon"];
  }

  // Se dispara cuando el elemento es insertado en el DOM
  connectedCallback() {
    this.render();
  }

  // Se dispara cuando el elemento es removido del DOM
  disconnectedCallback() {
    // Aquí podrías limpiar listeners u otros recursos
  }

  // Se dispara cuando alguno de los atributos en observedAttributes cambia
  attributeChangedCallback(name, oldValue, newValue) {
    if (name === "typeicon") {
      this.typeicon = newValue;
      this.render();
    }
  }

  // Método para actualizar la representación del componente
  render() {
    this.innerHTML = `
      <span class="icon-${this.typeicon}">[icono: ${this.typeicon}]</span>
    `;
  }
}

// Definir el elemento personalizado
window.customElements.define("wc-icon", WcIcon);

// Exportar la clase (opcional, pero recomendable para organización del código)
export default WcIcon;

Nota: No es obligatorio usar JS Modules con Web Components, pero es una buena práctica ya que facilita la organización y escalabilidad del proyecto.

3. Acciones internas propias del Web Component

Dentro del constructor y los métodos nativos, gestionamos las propiedades y el ciclo de vida del componente.

  • Constructor: Aquí inicializamos propiedades y creamos el Shadow DOM si lo necesitamos. Por ejemplo: constructor() { super(); this.typeicon = "default"; }
  • Gestionar y manejar atributos: Atributos como typeicon pueden cambiar. Cuando esto sucede, attributeChangedCallback nos permite responder ante esos cambios. Ejemplo: attributeChangedCallback(attr, oldValue, newValue) { if (attr === "typeicon") { this.typeicon = newValue; this.render(); } }
  • connectedCallback(): Se ejecuta cuando el elemento se agrega al DOM.
  • disconnectedCallback(): Se ejecuta cuando el elemento se remueve del DOM.
  • observedAttributes(): Es un getter estático que retorna la lista de atributos que queremos observar. En nuestro caso: static get observedAttributes() { return ["typeicon"]; }

Estas funciones nos permiten construir componentes dinámicos, reaccionar a cambios y mantener el código ordenado y coherente.

¿Qué es el Shadow DOM?

El Shadow DOM es una parte fundamental de los Web Components. Nos permite encapsular el marcado, el estilo y la funcionalidad de un componente, de modo que el CSS y el JavaScript del entorno donde se utiliza no afecten el interior del componente, y viceversa.

  • Definición:
    El Shadow DOM crea un árbol de DOM encapsulado asociado a un elemento, que no se ve afectado por el estilo o el script global de la página. Esto garantiza que el componente sea verdaderamente modular.
  • ¿Para qué sirve?:
    Sirve para evitar conflictos de nombres de clases, estilos CSS globales que sobreescriban la apariencia interna del componente, o comportamientos no deseados. Esto hace que el componente sea más fácil de mantener y reutilizar.
  • ¿Cómo implementarlo?:
    Dentro del constructor del Custom Element, podemos adjuntar un shadow root al elemento: constructor() { super(); // Creamos un shadow root en modo 'open' this.attachShadow({ mode: 'open' }); this.shadowRoot.innerHTML = ` <style> .icon { color: #1da1f2; /* Ejemplo: color Twitter */ font-size: 24px; } </style> <span class="icon">[icon]</span> `; }

Con esto, la <span class="icon"> y sus estilos se encapsulan dentro del shadow root, evitando conflictos con el resto de la página.

4. LitElement: La manera más sencilla de crear Web Components sin morir en el intento

Aunque es completamente posible crear Web Components en vanilla JS, la comunidad ha desarrollado librerías para simplificar el proceso. Una de las más populares es LitElement (de la familia de Lit), que permite:

  • Definir componentes con menos código «boilerplate».
  • Utilizar plantillas basadas en `template literals` para generar el DOM.
  • Reaccionar de forma automática a cambios en las propiedades del componente.
  • Integrar el shadow DOM sin fricción.

Definición: LitElement es una librería ligera y moderna para crear componentes web aprovechando las últimas características de JavaScript. Ayuda a mantener el código limpio, fácil de leer y muy mantenible.

¿Para qué sirve?:
LitElement hace que crear un Custom Element sea más ágil, con menos código repetitivo, y facilita la gestión de propiedades reactivas, estilos y funciones de ciclo de vida del componente.

¿Cómo implementarlo?:
Ejemplo básico con LitElement:

import { LitElement, html, css } from 'lit';

class MyIcon extends LitElement {
  static properties = {
    typeicon: { type: String },
  };

  static styles = css`
    .icon {
      font-size: 24px;
      color: #1da1f2;
    }
  `;

  constructor() {
    super();
    this.typeicon = 'default';
  }

  render() {
    return html`
      <span class="icon">[Icono: ${this.typeicon}]</span>
    `;
  }
}

customElements.define('my-icon', MyIcon);

Con LitElement, el código es más conciso y declarativo, lo que agiliza el desarrollo de componentes cada vez más complejos, el único incoveniente es que ultimamente google (la creadora de la libreria) la tiene un poco olvidada. Sin embargo es 100% recomendable su uso, aunque si vas a crear un proyecto grande, que mejor que crear tus propias herramientas basados en los standars con JS de toda la vida 😀

Conclusión

Los Web Components representan un paso firme hacia la estandarización y modularización del desarrollo frontend. Nos permiten crear elementos totalmente reutilizables, integrables en cualquier proyecto y libres de dependencias con frameworks específicos. Ya sea usando vanilla JS o librerías como LitElement, la curva de aprendizaje se ve recompensada por la claridad, mantenibilidad y escalabilidad que estos componentes aportan a nuestras aplicaciones web.

Nando Muñoz
Resumen de privacidad

Esta web utiliza cookies para que podamos ofrecerte la mejor experiencia de usuario posible. La información de las cookies se almacena en tu navegador y realiza funciones tales como reconocerte cuando vuelves a nuestra web o ayudar a nuestro equipo a comprender qué secciones de la web encuentras más interesantes y útiles. Para revisar nuestra política de privacidad en esta pagina.