Dans l'écosystème frontend moderne, la dépendance à des frameworks spécifiques comme React, Vue ou Angular est omniprésente. Cependant, un besoin croissant émerge pour des composants UI qui transcendent ces limites. Que vous mainteniez une application jQuery legacy, construisiez une architecture micro-frontend ou souhaitiez simplement garantir la portabilité de votre système de design, les Web Components offrent une solution native et standardisée. Ce guide explore comment construire une bibliothèque d'interfaces UI robuste et indépendante du framework en utilisant les Custom Elements et le Shadow DOM.
La philosophie centrale : Encapsulation et interopérabilité
Les Web Components ne sont pas une solution miracle à tous les problèmes, mais ils excellent dans la création d'éléments d'interface isolés et réutilisables. Cette technologie repose sur trois piliers principaux : les Custom Elements, qui permettent de définir de nouvelles balises HTML ; le Shadow DOM, qui offre un encapsulage du style et du balisage avec une portée limitée ; et les modèles HTML (HTML Templates), qui permettent une manipulation efficace du DOM.
En exploitant ces API, vous pouvez créer des composants qui ont une apparence et un comportement cohérents sur différents navigateurs et frameworks, sans la surcharge liée au diffing du DOM virtuel ou aux hooks de cycle de vie spécifiques à un framework.
Étape 1 : Définir l'élément personnalisé
La base de tout Web Component est la classe Custom Element. Cette classe étend HTMLElement et définit le comportement, le cycle de vie et les attributs du composant. Commençons par construire un composant app-button simple et réutilisable.
class AppButton extends HTMLElement {
constructor() {
super();
// Attacher le racine shadow pour l'encapsulation
this.attachShadow({ mode: 'open' });
this.render();
}
// Réagir aux changements d'attributs
static get observedAttributes() {
return ['variant', 'disabled'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
this.render();
}
}
// Rendre le DOM interne du composant
render() {
const variant = this.getAttribute('variant') || 'primary';
const disabled = this.hasAttribute('disabled');
this.shadowRoot.innerHTML = `
`;
}
}
// Enregistrer le composant
customElements.define('app-button', AppButton);
Dans cet exemple, nous utilisons static get observedAttributes() pour indiquer au navigateur quels attributs doivent déclencher un nouveau rendu. La méthode attributeChangedCallback assure que notre interface reste synchronisée avec les attributs HTML passés au composant.
Étape 2 : Maîtriser le Shadow DOM et le style
Le Shadow DOM est l'endroit où se produit la magie de l'encapsulation. Les styles définis à l'intérieur de :host ou dans l'arbre shadow ne fuient pas vers l'extérieur, et les styles externes ne viennent pas perturber l'intérieur. Cela est crucial pour les bibliothèques d'interfaces car cela empêche les conflits CSS dans les applications plus grandes.
Cependant, vous pouvez avoir besoin de permettre aux consommateurs de votre bibliothèque d'interfaces de styliser certaines parties spécifiques. C'est ici que l'élément <slot>> entre en jeu. Dans le code ci-dessus, nous avons utilisé <slot> à l'intérieur du bouton pour permettre aux utilisateurs de passer du contenu personnalisé, tel que des icônes ou du texte, tout en conservant la structure du bouton intacte.
Étape 3 : Gérer les événements dans un environnement isolé
Un piège courant pour les débutants est la gestion des événements à l'intérieur du Shadow DOM. Les événements qui se produisent à l'intérieur de l'arbre shadow (comme un clic sur un bouton) ne sont pas automatiquement accessibles dans le DOM léger (le composant parent) à moins qu'ils ne soient composés. Vous pouvez contrôler ce comportement en définissant le drapeau composed: true sur vos événements personnalisés.
handleClick() {
this.dispatchEvent(new CustomEvent('click', {
bubbles: true,
composed: true, // Permet à l'événement de sortir du shadow DOM
detail: { action: 'submit' }
}));
}
En définissant composed: true, vous vous assurez que l'application parente peut écouter les événements émis par votre composant, maintenant ainsi l'interopérabilité.
Conclusion
Construire une bibliothèque d'interfaces UI indépendante du framework en utilisant les Web Components est un investissement stratégique pour la maintenabilité à long terme. Cela réduit le couplage, simplifie les tests et garantit que votre système de design reste pérenne. Bien que la courbe d'apprentissage implique de comprendre les limites de l'encapsulation, la récompense est un ensemble de primitives d'interface hautement portables, performantes et cohérentes. Commencez petit avec un seul composant, expérimentez avec le Shadow DOM, et étendez progressivement votre bibliothèque pour couvrir l'ensemble de votre système de design.