Web Components
Impara i web component JavaScript: elementi personalizzati, Shadow DOM, template HTML, slot e callback del ciclo di vita, con esempi eseguibili.
I web component ti permettono di creare elementi HTML riutilizzabili e incapsulati — tag personalizzati come <user-card> o <star-rating> che portano con sé markup, stili e comportamento. Poiché sono costruiti su API native del browser, funzionano con qualsiasi framework (o senza framework) e mantengono i loro meccanismi interni isolati dal resto della pagina. Questa guida copre i tre elementi fondamentali dei web component, illustra come costruirne uno da zero e si conclude con gli hook del ciclo di vita e le best practice.
Quando usare i web component?
Ricorri ai web component quando vuoi un widget UI autonomo che:
- Funzioni ovunque — lo stesso
<my-widget>può essere inserito in una pagina React, Vue, HTML semplice o renderizzata lato server senza alcun passaggio di build. - Rimanga isolato — i suoi stili non possono fuoriuscire e gli stili della pagina non possono infiltrarsi, grazie allo Shadow DOM.
- Sia distribuito come file singolo — markup, stile e logica vivono insieme, così il componente è facile da distribuire e riutilizzare.
I design system, i widget incorporabili e le librerie di componenti condivisi sono i casi d'uso più comuni.
Introduzione ai Web Component
I web component sono un insieme di API della piattaforma web che ti permettono di creare nuovi tag HTML personalizzati, riutilizzabili e incapsulati. Tre tecnologie lavorano insieme:
- Custom Elements — definiscono nuovi tag HTML e il loro comportamento.
- Shadow DOM — incapsula il markup e gli stili di un componente in modo che siano circoscritti all'elemento.
- HTML Templates — dichiarano blocchi riutilizzabili di markup inerte che vengono istanziati a runtime.
Custom Elements
I Custom Elements definiscono nuovi tag HTML e il loro comportamento. Si basano sulle classi JavaScript: un elemento personalizzato è una classe che estende HTMLElement. Una volta registrato, il tag può essere utilizzato come qualsiasi elemento HTML standard.
<div>
<script>
class MyElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = '<p>Hello, World!</p>';
}
}
customElements.define('my-element', MyElement);
</script>
<my-element></my-element>
</div>Questo snippet dimostra come creare un elemento HTML personalizzato chiamato my-element. La classe MyElement estende HTMLElement, collega uno shadow DOM per incapsulare il suo contenuto e inserisce un semplice paragrafo con il testo "Hello, World!".
Shadow DOM
Lo Shadow DOM fornisce incapsulamento per i tuoi elementi personalizzati. Permette di collegare un albero DOM separato e nascosto (il shadow tree) a un elemento, mantenendo stili e markup circoscritti all'elemento stesso. Per un approfondimento, vedi il capitolo dedicato allo Shadow DOM JavaScript.
<div>
<script>
class ShadowElement extends HTMLElement {
constructor() {
super();
let shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
p { color: blue; }
</style>
<p>Shadow DOM content</p>
`;
}
}
customElements.define('shadow-element', ShadowElement);
</script>
<shadow-element></shadow-element>
</div>In questo esempio, la classe ShadowElement estende HTMLElement e collega una shadow root all'elemento. Il parametro mode: 'open' specifica che lo Shadow DOM è accessibile tramite JavaScript. Quando mode è impostato su 'open', la shadow root è accessibile tramite la proprietà element.shadowRoot. Se mode è impostato su 'closed', la shadow root non è accessibile dall'esterno, potenziando l'incapsulamento e impedendo agli script esterni di manipolare direttamente lo Shadow DOM.
All'interno dello Shadow DOM, definiamo alcuni stili e contenuto HTML. Gli stili (in questo caso, un paragrafo con testo blu) sono circoscritti allo Shadow DOM, garantendo che non influenzino altri elementi nella pagina. Questo incapsulamento è utile per creare componenti riutilizzabili con stile e comportamento coerenti, indipendentemente dal contesto in cui vengono utilizzati.
Usa stili locali all'interno dello Shadow DOM per prevenire la fuoriuscita degli stili e garantire che gli stili del componente siano circoscritti.
Per ulteriori informazioni su come circoscrivere CSS a un shadow tree — inclusi :host e le proprietà personalizzate CSS che possono attraversare il confine — vedi Shadow DOM styling.
HTML Templates
Gli HTML Templates ti permettono di definire blocchi riutilizzabili di HTML che sono inerti — vengono analizzati ma non renderizzati, e i loro script non vengono eseguiti — finché non li cloni e li inserisci a runtime. L'elemento <template> è il modo standard per farlo.
<div>
<template id="my-template">
<style>
p { color: red; }
</style>
<p>This is a template content</p>
</template>
<script>
const template = document.getElementById('my-template').content;
document.body.appendChild(template.cloneNode(true));
</script>
</div>Questo esempio mostra come usare gli HTML Templates. Il tag <template> contiene HTML e stili che non vengono renderizzati immediatamente. Lo script clona il contenuto del template e lo aggiunge al corpo del documento, rendendolo visibile.
Creare il Tuo Primo Web Component
Vediamo il processo per creare un web component completamente funzionale che può essere utilizzato in progetti diversi.
Passaggio 1: Definire il Componente
Crea una nuova classe che estende HTMLElement.
<div>
<script>
class MyComponent extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = 'Hello, World!';
}
}
customElements.define('my-component', MyComponent);
</script>
<my-component></my-component>
</div>Questo snippet pone le basi per un web component chiamato my-component. Definisce una classe che estende HTMLElement e collega uno shadow DOM, preparandolo a ulteriori personalizzazioni.
Passaggio 2: Aggiungere Stili e Template
Usa lo Shadow DOM per incapsulare stili e template.
<div>
<script>
class MyStyledComponent extends HTMLElement {
constructor() {
super();
let shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
p { font-size: 20px; color: green; }
</style>
<p>This is my styled component!</p>
`;
}
}
customElements.define('my-styled-component', MyStyledComponent);
</script>
<my-styled-component></my-styled-component>
</div>Questo snippet arricchisce my-styled-component aggiungendo stili circoscritti all'interno dello shadow DOM. Il paragrafo all'interno del componente è stilizzato con colore verde e dimensione del font aumentata.
Passaggio 3: Aggiungere Interattività
Aggiungi JavaScript per rendere il tuo componente interattivo.
<div>
<script>
class InteractiveComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
p { font-size: 20px; }
</style>
<p>This is interactive!</p>
<button>Click me</button>
`;
this.shadowRoot.querySelector('button').addEventListener('click', () => {
this.shadowRoot.querySelector('p').textContent = 'You clicked!';
});
}
}
customElements.define('interactive-component', InteractiveComponent);
</script>
<interactive-component></interactive-component>
</div>Questo esempio mostra come rendere interattivo un web component. InteractiveComponent include un pulsante che, quando cliccato, modifica il contenuto testuale di un paragrafo all'interno del componente.
Tecniche Avanzate per Web Component
Callback del Ciclo di Vita
Gli elementi personalizzati hanno callback del ciclo di vita che ti consentono di eseguire codice durante fasi specifiche della vita di un elemento.
<div>
<script>
class LifecycleComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.shadowRoot.innerHTML = '<p>Element added to page.</p>';
}
disconnectedCallback() {
console.log('Element removed from page.');
}
}
customElements.define('lifecycle-component', LifecycleComponent);
</script>
<lifecycle-component></lifecycle-component>
</div>Questo snippet dimostra l'uso delle callback del ciclo di vita nei web component. LifecycleComponent aggiorna il suo contenuto quando viene aggiunto alla pagina e registra un messaggio quando viene rimosso.
Gestione dei Cambiamenti degli Attributi
Reagisci ai cambiamenti negli attributi di un elemento definendo l'array observedAttributes e implementando il metodo attributeChangedCallback.
<div>
<script>
class AttributeComponent extends HTMLElement {
static get observedAttributes() {
return ['data-text'];
}
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = '<p></p>';
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'data-text') {
this.shadowRoot.querySelector('p').textContent = newValue;
}
}
}
customElements.define('attribute-component', AttributeComponent);
</script>
<attribute-component data-text="Initial text"></attribute-component>
<script>
const element = document.querySelector('attribute-component');
setTimeout(() => {
element.setAttribute('data-text', 'Updated text');
}, 2000);
</script>
</div>Questo esempio mostra come gestire i cambiamenti degli attributi in un web component. AttributeComponent aggiorna il suo contenuto interno in base ai cambiamenti dell'attributo data-text. Nota che attributeChangedCallback si attiva solo per gli attributi elencati in observedAttributes — senza tale lista, i cambiamenti degli attributi vengono ignorati.
Le quattro callback del ciclo di vita degli elementi personalizzati sono:
| Callback | Quando viene eseguita |
|---|---|
constructor() | Quando l'elemento viene creato o aggiornato. Imposta qui lo stato iniziale, ma evita di accedere agli attributi o al DOM figlio. |
connectedCallback() | Ogni volta che l'elemento viene inserito nel documento. Il posto migliore per renderizzare e aggiungere listener di eventi. |
disconnectedCallback() | Ogni volta che l'elemento viene rimosso dal documento. Rimuovi qui listener e timer. |
attributeChangedCallback(name, oldVal, newVal) | Quando un attributo osservato cambia. Richiede un getter statico observedAttributes. |
Distribuzione dei Figli con gli Slot
Un <slot> permette a un componente di renderizzare il markup che un utente inserisce tra i suoi tag, così i consumatori possono passare il proprio contenuto. Questa è la base della composizione con i web component — vedi Shadow DOM, slot e composizione per il quadro completo.
<div>
<script>
class CardBox extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
.box { border: 1px solid #ccc; padding: 8px; }
</style>
<div class="box"><slot>Default content</slot></div>
`;
}
}
customElements.define('card-box', CardBox);
</script>
<card-box>Passed-in content</card-box>
<card-box></card-box>
</div>Il primo <card-box> renderizza "Passed-in content" all'interno del riquadro stilizzato; il secondo ricade sul contenuto predefinito dello slot "Default content".
Best Practice per i Web Component
Usa lo Shadow DOM con Saggezza
Incapsula stili e script all'interno dello Shadow DOM per prevenire conflitti con altri elementi della pagina.
Segui le Convenzioni di Denominazione
Gli elementi personalizzati devono contenere un trattino nel nome (ad esempio, my-card, non mycard) per evitare conflitti con gli elementi HTML standard attuali e futuri. La registrazione di un nome privo di trattino genera un errore.
Mantieni i Componenti Modulari
Crea componenti piccoli e riutilizzabili per mantenere il codice manutenibile e scalabile.
Evita gli Stili Globali
Usa stili locali all'interno dello Shadow DOM per prevenire la fuoriuscita degli stili e garantire che gli stili del componente siano circoscritti.
Documentazione e Test
Documenta bene i tuoi componenti e scrivi test per assicurarti che funzionino come previsto su browser e casi d'uso diversi.
Conclusione
I web component sono una funzionalità versatile e potente nello sviluppo web moderno, che permette la creazione di elementi personalizzati riutilizzabili e incapsulati. Comprendendo e utilizzando Custom Elements, Shadow DOM e HTML Templates, puoi creare applicazioni web modulari, manutenibili e scalabili. Questa guida ti ha fornito le conoscenze e gli esempi necessari per iniziare con i web component, garantendoti di poterli implementare efficacemente nei tuoi progetti.
Per approfondire, esplora lo Shadow DOM JavaScript, come interagiscono Shadow DOM ed eventi, e l'elemento <template> in dettaglio.