© Andrew Krasovitckii/Shutterstock.com
Überlegungen zum Praxiseinsatz von Custom Elements

Ein Grundgesetz für Web Components


In schöner Regelmäßigkeit finden sich Web Components erst in aller Munde und kurz darauf in der Versenkung wieder. Die Idee hinter entwicklerdefinierten HTML-Elementen scheint für viele Entwickler attraktiv zu sein, während im Alltag offenbar der Frust überhandnimmt. Der Grund: fehlgeleitete Erwartungen und daraus folgend ein unsachgemäßer und entsprechend frustrierender Einsatz von Web Components.

Doch all der Frust muss nicht sein, wenn wir Web Components so einsetzen, dass sie ihre Vorteile ausspielen. Dieser Text legt ein fünf Paragrafen langes Grundgesetz für Web Components dar, das Regeln für den schmerzfreien Bau und Einsatz von Custom Elements aufstellt. Die Regeln sind, wie es bei einem Grundgesetz üblich ist, eher allgemeiner Natur, fußen auf Erfahrungen aus der Vergangenheit und sind offen für Änderungsanträge. Die zentrale Überlegung hinter dem Regelwerk ist eine genaue Analyse des Wesens und der Trade-offs von Web Components, die nicht unbedingt deckungsgleich mit anderen, vielleicht sogar verwandten Konzepten von Komponenten sind.

Was sind Web Components … und was sind sie nicht?

Unter Web Components verstehen wir HTML-Elemente, die nicht den Webstandards entspringen, sondern von Webentwicklern per JavaScript definiert wurden. In allen modernen Browsern steht der dafür nötige Mix aus APIs zur Verfügung (Listing 1).

Listing 1: Hello World mit Web Components

window.customElements.define("hello-world", class extends HTMLElement { constructor() { super(); window.alert("Hello!"); } }); // <hello-world> ist bereit zum Einsatz!

Wir schreiben eine ECMAScript-Klasse, leiten von HTMLElement (oder einer seiner Subklassen) ab, füttern die Klasse zusammen mit dem gewünschten Tagnamen in customElements.define() und schon können wir <hello-world> an beliebigen Stellen unseres Webdokuments verwenden. Die auf diese Weise definierten HTML-Elemente unterliegen einigen technisch motivierten Regeln (der Tagname muss einen Bindestrich enthalten, es gibt keine Self-closing Tags bei Custom Elements usw.), aber diese stellen nicht das in diesem Artikel beschriebene Grundgesetz für Web Components dar. Für uns ist vielmehr eine andere Erkenntnis relevant: Web Components sind HTML-Elemente und nichts weiter. Das klingt angesichts des obigen Codebeispiels offensichtlich, da es buchstäblich Define Custom Elements enthält. Doch die Folgen dieser Erkenntnis sind nicht unbedingt ebenso offensichtlich.

Die Diskussion um Web Components leidet ein wenig unter überladenen Begrifflichkeiten und speziell unter dem doch sehr allgemeinen Begriff „Komponente“. Ähnlich wie „Klasse“ oder „Objekt“ bezeichnet Komponente etwas in einem Programm, über dessen grundsätzlichen Charakter sich alle Entwickler der Welt einig sein dürften, doch die konkreten Ausprägungen von Klassen, Objekten und Komponenten sind je nach Programmiersprache oder Projekt extrem unterschiedlich. Und diese Unterschiede sind wichtig! Wenn wir in einer Sprache wie C# bei Klassen auf Features wie protected und abstract zurückgreifen können, uns diese Features in JavaScript aber fehlen, wird das Ergebnis sein, dass unsere Klassen (und vermutlich sogar die die Klassen verwendenden Programme selbst) in C# sehr anders ausfallen werden als äquivalente JavaScript-Programme und -Klassen. Damit ist nicht gesagt, dass die eine Programmiersprache Klassen besser macht als die andere, sondern erst einmal nur, dass unterschiedliche Programmiersprachen das Konzept Klasse unterschiedlich umsetzen. Diese Unterschiede haben einen Einfluss darauf, was wir als Entwickler mit dem Konzept Klasse jeweils anstellen – und das wiederum passiert nur dann ohne größere Probleme, wenn wir sehr genau wissen, mit welchen konkreten Ausprägung des Konzepts Klasse wir es jeweils zu tun haben. Wenn wir einfach unsere Idee einer Klasse von C# nach JavaScript transplantieren, wird das nicht ohne Blut, Schweiß und Tränen vonstattengehen. Und das Gleiche gilt natürlich auch für das Konzept Komponente.

React vs. Web Components – Komponentenkonzepte unter der Lupe

Es ist nicht damit getan, dass wir feststellen, dass der Begriff Komponente in unterschiedlichen Kontexten unterschiedliche Bedeutungen hat, denn nicht alle Komponentenkonzepte sind gegeneinander austauschbar. Jede technische Lösung für jedes technische Problem enthält Trade-offs – das eine Tool ist besonders performant, das andere ist besonders einfach zu bedienen, das nächste ist besonders billig und manches Tool zeichnet sich einfach dadurch aus, dass es auf eine bestimmte Art und Weise arbeitet.

Komponenten finden Webentwickler heutzutage an allen Ecken und Enden vor, denn neben Web Components implementieren auch so ziemlich alle Front- und Backend-Frameworks ihre jeweiligen Komponentenkonzepte. Vergleichen wir doch anhand eines einfachen Beispiels mal eine Web Component mit einer React-Komponente: Wir bauen eine Komponente namens „Say Hello“, die den Text „Hello“ einem anderen Text voranstellt und das Endergebnis in HTML rendert. Fangen wir mit React an:

const SayHello = ({ children }) => <span>Hello { children }</span>;

Sehr viel einfacher wird’s nicht mehr: eine React-Komponente ist eine Funktion, die ein paar Inputs erhält und als Output eine DOM-Struktur liefert. Mehrfache Aufrufe der Komponentenfunktion liefern ein immer neues DOM, aus dem die React-Runtime die jeweils nötigen Updates herausrechnet – wir als Komponentenentwickler beschreiben immer einen Sollzustand für die Komponente, den die React Runtime mit dem Istzustand vergleicht und bei Bedarf Änderungen vornimmt. Als syntaktischen Zucker liefert React auch noch eine JavaScript-Syntax-Erweiterung mit, die das Formulieren von DOM-Elementen durch XML-artige Tags ganz besonders einfach macht. Alles ist darauf ausgelegt, dass es die Entwickler möglichst leicht haben. Wir müssen immer nur unseren Wunschzustand beschreiben und bekommen dazu auch noch eine goldene Syntax-Brücke gebaut.

Die Implementierung der gleichen Grundidee einer Komponente als Web Component offenbart einen ganz anderen Aufbau und auch eine komplett andere Funktionsweise, wie in Listing 2 zu sehen ist.

Listing 2: Eine einfache Web Component

window.customElements.define("say-hello", class extends HTMLElement { constructor() { super(); this.content = this.attachShadow({ mode: "open" }); // Shadow DOM this.content.append( "Hello ", document.createElement("slot") // <slot>-Element ); } });

Die Klasse als Input für customElements.define() kennen wir bereits. Neu ist für uns hier das Shadow DOM und das slot-Element. Shadow DOM ist ein spezieller DOM Tree in einer Web Component, der für den inneren Aufbau der Komponente zuständig ist. Shadow DOM trennt interne Implementierungsdetails einer Komponente vom Rest der Welt, damit Entwickler nicht versehentlich mit CSS oder JavaScript auf interne Details Einfluss nehmen. Beim Rendering nimmt das Shadow DOM den Platz des eigentlichen HTML-Inhalts ein, also des Content zwischen öffnendem und schließendem Tag der Komponente. Das Shadow DOM konstruieren wir ganz einfach mit Hilfe der altbekannten DOM APIs, hier mit der append()-Methode. Das dort verwendete HTML-Element <slot> ist speziell für den Einsatz im Shadow DOM gedacht und bildet ein Portal, durch das der durch das Shadow DOM verdeckte Inhalt wieder in das Shadow DOM hineingerendert wird. Dadurch lässt sich der Content zwischen öffnendem und schließendem Tag der Komponente gleichsam in einen Wrapper verpacken: Shadow DOM definieren, dadurch den Inhalt des Elements verdecken, aber den verdeckten Content via <slot> in die Verdeckung wieder hineinprojizieren. In diesem Fall besteht die „Verdeckung“ aus dem Text „Hello“, ganz wie wir es geplant hatten, sowie dem Slot als Projektionsfläche für den hinter „Hello“ gerenderten Text.

Was fällt im Vergleich denn auf? Klar, die Web Component ist, wie die längere Beschreibung des Codes zeigt, durchaus komplizierter und der Code ist auch länger. Aber Bill Gates hat nicht zu Unrecht einst angemerkt, dass die Zeilenanzahl beim Programmieren eine etwa so sinnvolle Qualitätsmetrik ist wie die Gewichtszunahme beim Flugzeugbau. Neben...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang