Componentes Web são um conjunto de normas atualmente sendo produzidos por engenheiros do Google como também uma especificação da W3C que permitem a criação de componentes reutilizáveis em documentos e aplicações web. A intenção por trás deles é trazer a engenharia de software baseada em componentes para a Web. O modelo de componentes permite encapsulamento e interoperabilidade de elementos HTML individuais.
O suporte para os componentes web está presente nos principais navegadores web, com polyfills disponíveis para os demais sem suporte.
Componentes Web consistem em 4 elementos principais que podem ser utilizados em conjunto ou separadamente:
A definição do componente:
export class MyCounter extends HTMLElement {
#count;
#label;
#button;
connectedCallback() {
const start = Number(this.getAttribute('start'));
this.#count = Math.max(0, start || 5);
// Se não possuir Declarative Shadow Root
if (!this.shadowRoot) {
this.attachShadow({ mode: 'open' }).innerHTML = `
<style>
:host {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.5rem;
height: 100vh;
}
[part~=label] {
font: 1.125rem Noto, sans-serif;
}
</style>
<data part="label"></data>
<button part="count">Contar</button>
`;
}
this.#label = this.shadowRoot.querySelector('[part~=label]');
this.#button = this.shadowRoot.querySelector('[part~=count]');
this.#button.addEventListener('click', this.decrement.bind(this));
this.requestUpdate();
}
requestUpdate() {
this.#label.textContent = this.text;
this.#label.value = this.#count;
this.#button.disabled = this.#count === 0;
}
get text() {
switch (this.#count) {
case 0: return `Fim.`;
case 1: return `Encerra no próximo clique!`;
default: return `Encerra em ${this.#count} cliques.`;
}
}
decrement() {
if (this.#count > 0) {
this.#count--;
}
this.requestUpdate();
}
}
customElements.define('my-counter', MyCounter);
E o uso:
<my-counter start="5"></my-counter>
Ou com Declarative Shadow DOM (DSD) para uso com Server-Side Rendering (SSR):
<my-counter start="5">
<template shadowrootmode="open">
<style>
:host {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.5rem;
height: 100vh;
}
[part~=label] {
font: 1.125rem Noto, sans-serif;
}
</style>
<data part="label" value="5">Encerra em 5 cliques.</data>
<button part="count">Contar</button>
</template>
</my-counter>