Ciclo de vida de WebComponents

Fases por las que pasa un WebComponent


Durante la carga de una página y mientras la utilizamos, los WebComponent pasan por una serie de fases que se conocen como el ciclo de vida del WebComponent. Conociendo este ciclo y las fases que lo componen, es mucho más fácil predecir el funcionamiento de los WebComponents y trabajar con ellos.

  • Fase 1️⃣: Componente no creado, ni insertado en el DOM
  • Fase 2️⃣: Componente creado, pero no insertado en el DOM
  • Fase 3️⃣: Componente creado e insertado en el DOM

Entremos un poco más en detalle.

¿Que es el ciclo de vida?

El ciclo de la vida de un humano, son las fases por las que pasa: nacimiento, crecimiento, reproducción y muerte. De la misma forma, un componente pasa por sus propias fases. En el siguiente diagrama se puede ver los saltos de fase, y los métodos especiales que existen en el contexto de WebComponents:

WebComponents: Lifecycle Diagram

Estos métodos especiales los podemos implementar en la clase del componente. Sin embargo, dichos métodos no se llaman manualmente (como los que creamos los desarrolladores), sino que son unos métodos que se disparan automáticamente cuando el componente llega a una fase concreta de su «ciclo de vida».

Métodos del ciclo de vida

Los métodos del ciclo de vida de un WebComponent que podemos escribir en una clase, y que aparecen en el gráfico anterior, son los siguientes:

Característica ¿Cuándo se ejecuta?
constructor() Cuando se crea el custom element, y previamente ha sido definido en el registro.
connectedCallback() Cuando el custom element se ha insertado en el documento HTML (DOM).
disconnectedCallback() Cuando el custom element se ha eliminado del documento HTML (DOM).
adoptedCallback() Cuando el custom element se ha movido a un nuevo documento (ej: iframes).
attributeChangedCallback() Cuando se ha modificado un atributo observado del componente. Ver atributos

Veamos cada uno de estos métodos detenidamente.

Construcción del componente

Cuando creamos un Custom Element, inicialmente el navegador lo considera un HTMLElement, es decir, una etiqueta HTML estándar. Sin embargo, si se cumple la condición de que está definida en el registro global de customElements del navegador, entonces intentará actualizarla a Custom Element.

El método constructor() de un WebComponent tiene la misma función que en una clase de programación. Se ejecutará cada vez que se cree un Custom Element particular, y que previamente haya sido definido en el registro global del navegador con customElements.define(). Por lo tanto, si creamos varias etiquetas de un componente (instancias), se ejecutará una vez por cada una de ellas.

Esto ocurrirá de forma automática si el custom element se creó después de haber sido registrado mediante customElements.define(). Si se creó antes de registrarlo, tendremos que hacerlo de forma manual mediante customElements.upgrade(). Tienes más sobre esto en Registro de Custom Elements.

El objetivo del constructor es hacer ciertas tareas de inicialización rápidas o tareas iniciales, como la creación del Shadow DOM (lo veremos más adelante). Es esencial que en el constructor del componente se hagan sólo las tareas más prioritarias y ligeras.

En el caso de incluir lógica en el constructor que pueda llegar a modificar el DOM o los atributos de un componente, podría afectar al rendimiento o aparecernos errores como el siguiente:

Uncaught DOMException: Failed to construct 'CustomElement': The result must not have children

Como norma general, aplaza todo lo que puedas al método connectedCallback().

Inserción en el DOM

El método connectedCallback() es una suerte de segundo constructor() que se ejecuta cuando el custom element es conectado al documento HTML (DOM), es decir, cuando el componente es insertado o añadido en el documento HTML principal.

Piensa, por ejemplo, que podemos crear un elemento HTML personalizado en una variable, que no esté incluido en el documento HTML:

// En este momento, el Custom Element ya es reconocido por el navegador
customElements.define("app-element", AppElement);

// En este momento, se ejecuta el constructor() del componente
const component = document.createElement("app-element");

// En este momento, se ejecuta el connectedCallback() del componente
document.body.append(component);

Esta característica lo hace realmente útil para incluir, por ejemplo, tareas relacionadas con la renderización o dibujo visualmente. Por esta razón, también resulta interesante desplazar a este método ciertas tareas que es posible que no lleguen a necesitarse de manera inmediata, mejorando así el rendimiento general.

Ten en cuenta que si un custom element es movido a otra parte del DOM, se desconectará y volverá a conectarse al DOM, pasando por los métodos connectedCallback() y disconnectedCallback() correspondientes.

Eliminación del DOM

Por contrapartida, el método connectedCallback() tiene un método opuesto o contrario: disconnectedCallback(). Mientras que el primero es llamado cuando insertas un custom element en el documento HTML, el último es llamado cuando un custom element es eliminado del DOM del documento HTML.

// Localizamos un componente en el documento HTML
const element = document.querySelector("app-element");

// En este momento, se ejecuta el disconnectedCallback() del componente
element.remove();

El método disconnectedCallback() puede ser realmente útil para realizar tareas importantes de finalización que, de lo contrario, estarían consumiendo recursos respecto a ese elemento.

Mover a otro documento

El método adoptedCallback(), de uso menos frecuente, tiene sentido cuando se trabaja en contextos multidocumento, y se dispara cuando un custom element se mueve de un documento HTML a otro documento HTML diferente. Para moverlo, se utiliza el método .adoptNode().

Este método es muy útil cuando se trabaja con elementos <iframe>, por ejemplo.

Detección de cambios de atributos

Esta fase del ciclo de vida, controlada desde el método attributeChangedCallback(), se dispara cuando un atributo HTML del componente que está siendo observado cambia de valor. Lo explicamos en el post de Reactividad en Atributos HTML.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev