Kolumne: EnterpriseTales

Web Components API: Mach’s dir nicht so schwer!

Hendrik Müller

© S&S_Media

Die Enterprise-Welt ist geprägt von webbasierten Formularanwendungen. Wenn man sich dann überlegt, mehrfach in unterschiedlichen Anwendungen die gleichen UI-Komponenten zu verwenden, ist der Einsatz von Framework-agnostischen Web Components als technische Grundlage naheliegend. Web Components ermöglichen eine einfache Wiederverwendung und Integration, doch es lauern auch ein paar Stolpersteine, die es zu vermeiden gilt.

Warum und wofür Web Components?

Web Components umfassen eine Menge von Webstandards, die es ermöglichen, eigene HTML-Elemente zu definieren. Der große Vorteil von Web Components ist die Verwendung standardisierter Schnittstellen, um mit dem Rest der Webseite zu kommunizieren. Entsprechend lassen sie sich leicht in unterschiedliche Technologien integrieren. Komponenten aus Frameworks wie Angular oder React sind hingegen zunächst einmal in ihrem eigenen Kosmos gefangen.

Man kann die Standards, die Web Components umfassen, also als Integrations- und Isolationstechnologie betrachten. Primär gibt es zwei große Use Cases für den Einsatz von Web Components: zum einen für mehrfach wiederverwendbare Designelemente und Widgets, zum anderen zur Integration und Isolation von Teilbereichen innerhalb der Seite, z. B. zur Umsetzung von Micro Frontends

Der Einsatz von Web Components ist also dann sinnvoll, wenn man die gleichen Komponenten in verschiedenen Kontexten wiederverwenden möchte und sich diese eventuell sogar technologisch unterscheiden. Dabei kann es sich sowohl um unterschiedliche SPAs handeln als auch um serverseitig generiertes HTML oder auch Content aus einem CMS.

Da das Web Components API nach außen aus HTML und DOM-Strukturen besteht, trifft es genau den kleinsten gemeinsamen Nenner, mit dem eigentlich jedes Framework umgehen kann. Man spricht auch von Framework-agnostisch.

Das klingt doch super, dann kann man ja eigentlich alles mit Web Components bauen? Man kann das sicherlich tun, aber Web Components sind anders als Angular, React oder Vue kein umfassendes Framework, sondern erst einmal nur ein Standard zum Spezifizieren eigener HTML-Elemente. Web Components sind sehr low-Level, ein Compile Target, wenn man so will. Es gibt Frameworks wie LitElement [1] oder Stencil [2], die es einem sehr leichtmachen, Web Components umzusetzen. Aber auch diese Frameworks haben eher einen Fokus auf einzelne Komponenten als auf Applikationen im Ganzen. Web Components spielen ihre große Stärke wirklich dann aus, wenn sie in unterschiedlichen Kontexten verwendet werden sollen, oder wenn man eine gewisse Isolation benötigt. Wenn keines von beidem zutrifft, sind Web Components wahrscheinlich nicht die Lösung für das Problem, das man hat.

Daraus, dass Web Components besonders nützlich sind, wenn man sie wiederverwenden kann, ergibt sich auch, dass sie meist eher keine fachliche Schnittstelle haben. Denn fachliche Komponenten sind oftmals nicht in verschiedenen Kontexten sinnvoll. Aber wenn doch, wie es zum Beispiel bei einem Log-in der Fall ist, dann haben sie kein API nach außen.

Anforderungen an eine Web Component

Von jetzt an gehen wir bei Web Components von relativ „dummen“ Designelementen, insbesondere Formularelementen, aus. Für diese lassen sich generell drei Anforderungen ausmachen, die eigentlich immer für eine Komponente gelten:

  • Design: Optik ist ein wichtiger Teil einer Web Component. Denn sie haben eigentlich immer etwas mit Visualisierung im Browser zu tun.
  • UX: Web Components, insbesondere Formularelemente, dienen oftmals der Interaktion mit dem Benutzer. Entsprechend sollten Dinge wie Tab-Navigation oder kluges Autocomplete bei Eingaben bedacht werden.
  • Wartbarkeit: Wie für jedes Stück Software gilt insbesondere für eine mehrfach verwendete Komponente, dass sie gut wartbar sein soll. Das bedeutet: einfaches und kompaktes Public API, sinnvolles Schneiden von Funktionalität und Vermeidung unnötiger Komplexität.

Kompetenzen im Team

Die Anforderungen Design, UX und Wartbarkeit unterscheiden sich zum Teil recht stark in den Kompetenzen, die im Team vorhanden sein sollten. Es ist wahrscheinlich nicht sinnvoll, die Umsetzung einer Komponente auf mehrere Leute aufzuteilen, aber durch einen guten Review-Prozess kann man dafür sorgen, dass jede Komponente aus allen drei Blickwinkeln betrachtet wird.

Unter den Frontend-Entwicklern gibt es gefühlt zwei ähnlich große Gruppen mit Graustufen dazwischen. Die eine Gruppe Frontend-Entwickler hat ein gewisses Gefühl für Design und UX oder ist sogar mal Designer, Mediengestalter oder Ähnliches gewesen. Die andere Gruppe der Frontend-Entwickler besteht eher aus typischen Technikern. Sie haben oft ursprünglich Backend-Entwicklung gemacht und legen bei Frontend-Entwicklung einen ganz anderen Fokus: Softwarearchitektur, Performance, Codequalität und vielleicht auch technische Spielerei.

Es ist sicherlich von Vorteil, aus beiden genannten Gruppen einen Entwickler im Team zu haben, um ein weites Feld der Anforderungen gut abzudecken.

Design und Styling

Da die Optik einer Web Component zumeist ziemlich relevant ist, sollte man das Thema Styling nicht außer Acht lassen. Ich habe oftmals das Gefühl, dass Styling als trivial abgestempelt wird, was ich nicht so sehe. Styling ist einfach, wenn man es kann. Und da CSS ja doch ein bisschen umfangreicher ist, braucht man schon einen Entwickler, der wirklich Lust darauf hat. Zum einen muss der Entwickler in der Lage sein, vermutlich eher statische Designs in Form von Screenshots oder Ähnlichem zu verstehen und die Intention des Designers nachvollziehen zu können. Und zum anderen muss er auch in der Lage sein, diese dann mit dem Wissen um technische Restriktionen umzusetzen. Ansonsten ist die Gefahr recht hoch, dass erstens das Styling schnell unwartbar wird, weil es nur „irgendwie“ funktioniert und dass zweitens schnell Edge Cases nicht betrachtet werden und zum Beispiel zu lange Texte nicht gut im Design funktionieren. Teile der Web-Components-Spezifikation sind zwar auch Shadow-DOM und Slots, womit man das CSS gut auf Komponenten einschränken kann – das verhindert aber nur Auswirkungen auf die Umgebung, die Komponente selbst wird dadurch trotzdem nicht wartbarer.

Die Wartbarkeit einer Web Component

Die Wartbarkeit einer Web Component lässt sich in zwei Teile zerlegen. Zum einen die Wartbarkeit von innen: Ein anderer Entwickler muss die Komponente einfach verstehen, erweitern oder Bugs fixen können. Zum anderen die Wartbarkeit nach außen: Die Pflege des Public API. Denn wenn Web Components für Designelemente verwendet werden, sind sie effektiv eine gesharte Library. Der große Vorteil von Web Components, nämlich dass man sie in verschiedenen Kontexten sowohl client- als auch serverseitig verwenden kann, ist ja nur dann gegeben, wenn alle konsumierenden Anwendungen gegen ein stabiles API arbeiten können. Sonst ist die Wiederverwendbarkeit nicht wirklich gegeben. Das gilt vor allem für Lösungen, die nicht communitygetrieben sind, denn hier kann eine unbedachte Änderung schnell durchrutschen.

Aber was genau ist denn eigentlich das API einer Web Component? Wenn man sich die Verwendung einer Web Component anschaut, lässt sich ihr API in drei Bereiche aufteilen:

  • HTML-API: Also das HTML-Element, seine Attribute und Slots. Dieses API kann auch serverseitig genutzt werden, indem HTML generiert wird. Dieses API ist wirklich statisch. Eigene HTML-Elemente müssen im Browser registriert werden, und dann wird die Web Component initial gerendert.
  • DOM/JavaScript-API: Das Interface, das zur Laufzeit zur Kommunikation mit JavaScript genutzt werden kann. Es umfasst auch alle Möglichkeiten des HTML-API, da man auch zur Laufzeit HTML-Fragmente ins DOM einhängen kann. Wichtiger aber sind die dynamischen Aspekte, also die Zustandsveränderung der Web Component per Methodenaufruf, und das Ändern von Properties, Attributen und Slots.
  • Styling: Wenn eine Web Component auch CSS mitbringt, was bei einem Designelement meist der Fall ist, dann hat dies unter Umständen auch Einfluss auf andere Komponenten. Beim Styling muss man darauf achten, dass die eigene Web Component ihre Umwelt nicht „aus Versehen“ beeinflusst. Bei Kindelementen kann es jedoch manchmal gewollt sein, dass es Wechselwirkungen gibt. Zudem hat man mit Shadow-DOM viele Möglichkeiten, das Styling zu beeinflussen, aber es gibt auch diverse Nachteile. Ein Scoping via BEM oder Ähnlichem sollte in vielen Fällen ausreichen. Zudem sollte man darauf achten, dass man keine „nach außen“ gerichteten Styles verwendet, also keine Margins oder Flex-Item-Eigenschaften.

Herausforderung API-Design

Dass das Designen eines guten API nicht gerade die einfachste Aufgabe ist, ist ja eigentlich jedem Softwareentwickler bekannt. Ein API soll möglichst simpel sein. Das kann man beispielsweise durch die Verwendung einfacher Strukturen und opaker Referenzen in den Methodenparametern und Return-und Property-Werten erreichen. Zudem gilt auch: Ein API sollte so klein wie möglich, aber nicht übergeneralisiert sein. Und wie bei jedem anderen API verhält es sich auch bei Web Components: Man sollte keine Superkomponente bauen, sondern lieber mehrere spezielle Komponenten. Solange man selbst der einzige Konsument seines API ist, sind Änderungen seltener ein Problem. Wirklich unangenehm wird es eigentlich erst, wenn es fremde Konsumenten gibt, denn dann gilt auf einmal: „Change ist teuer“. Im Fall von gesharten Web Components trifft genau das zu. Entsprechend sollte das API einer Web Component möglichst stabil sein oder zumindest abwärtskompatibel bleiben, damit alle Konsumenten einfach updaten können. Jede Sonderlocke für einen Konsumenten muss eventuell lange gepflegt werden, entsprechend ist es ratsam, bei neuen Features auch mal „nein“ zu sagen. Stattdessen kann lieber eine private Spezialkomponente für einen Konsumenten gebaut werden.

Wenn man sich das alles vor Augen hält, merkt man, dass es ganz schön schwierig ist, ein gutes API für eine Web Component zu designen.

Versionierungshölle oder für immer abwärtskompatibel?

Sobald wir unsere Web Components in Produktion bringen, haben wir eigentlich zwei Möglichkeiten, um mit dem Public API umzugehen:

  • Wir versionieren die Komponenten – jede Änderung des API wird also als neue Version der Komponente zur Verfügung gestellt.
  • Wir bleiben „für immer“ abwärtskompatibel.

Beides ist aufwendig. Versionierung führt dazu, dass man mehrere Versionen gleichzeitig pflegen und zudem auch noch ein Upgrade-Pfad bereitgestellt werden muss. Außerdem ist das Abkündigen einer Version eventuell schwierig, wenn wichtige Konsumenten aus irgendwelchen Gründen nicht updaten können.

Versionierung von Designelementen als Web Components bringt als weitere Komplexität mit sich, dass es zwei Versionen derselben Komponente auf derselben Seite geben könnte. Wenn man den Anspruch an sich stellt, dass an jeder verwendeten Stelle konsistentes Design gewünscht ist, muss natürlich auch sichergestellt sein, dass die beiden Versionen identisch aussehen. Die Alternative zu regelmäßiger Versionierung ist gar keine Versionierung. Stattdessen kann eine Variante eines Rolling-Release gefahren werden. Beim klassischen Rolling-Release gibt es nur eine Version, nämlich die aktuelle. Doch auch ein solches Verfahren ist kompliziert, denn das API darf nie in einer Form brechen, dass ein Konsument kaputt geht. Eine Art Consumer-driven Contract könnte hier helfen, um festzulegen, welcher Teil des API überhaupt noch verwendet wird. Zudem steigt bei zunehmender Komplexität der Komponenten die Gefahr, dass die Implementierung kritische Fehler hat, oder noch schlimmer: des einen Konsumenten Bug ist des anderen Konsumenten Feature. Um solche Situationen zu vermeiden, sollte man also seine Komponenten möglichst in sich geschlossen mit einem sehr simplen API bauen.

Sowohl Versionierung als auch Rolling-Release bringen also ihre Schwierigkeiten mit sich.

Das hat schon jemand anderes erledigt

Der Rolling-Release-Ansatz scheint für viele Menschen in der Enterprise-Welt absurd: „Dann hat man ja keine Kontrolle mehr.“ Das ist zwar richtig, aber trotzdem nur die halbe Wahrheit. Generell hat man als Konsument oftmals wenig Kontrolle über fremde Schnittstellen. Aber solange die Schnittstelle stabil ist, sollte das auch gar nicht so schlimm sein. Dann geht es eigentlich nur noch um Vertrauen. Das Gute ist, dass jeder Entwickler von Webanwendungen generell viel Vertrauen haben muss, z. B. in die Browserhersteller, weil er zumindest im Endkundengeschäft keine Kontrolle darüber hat, welche Browser verwendet werden. Das ist zwar auch häufig mit Schmerzen verbunden, aber in der Regel funktioniert es mittlerweile gut genug. Muss es ja auch, da man keine Wahl hat.

Diesen Umstand kann man auch zu seinem Vorteil nutzen, denn gerade in Bezug auf Formulare und Input-Felder muss man das Rad nicht neu erfinden. Die Webstandards bieten hier schon sehr viel, und da die Browserhersteller die Implementierung pflegen, kann man von weniger eigenem Pflegeaufwand ausgehen.

Entsprechend kann man einen einfachen Wrapper um ein <input> verwenden, anstatt sein eigenes Text-Input-Feld mit eigenen Events, Attributen und Validierung komplett selbst zu bauen. Der Vorteil hierbei ist, dass, je nachdem, was das Input-Feld alles können soll, es quasi kein Public API gibt, bis auf den Tag-Name und den Slot für das <input>. So kann der Wrapper mit dem Rest der Applikation über das <input> kommunizieren und gleichzeitig zusätzliches Styling oder Eingabehilfen hinzufügen:

<fancy-input>
  <input type="text"/>
</fancy-input>

Gleiches gilt natürlich auch für <select> und <option>. So können viele Eingabeelemente umgesetzt werden, indem einfach Wrapper-Elemente ein Standard-Eingabeelement erweitern. Die Komplexität kann dabei sehr klein gehalten werden, da kein umfangreiches API eines Input-Elements nachgebaut werden muss.

Fazit

Der Einsatz von Web Components lohnt sich dann, wenn man auch von ihren Vorteilen, insbesondere der Framework-Unabhängigkeit, profitieren kann. Das ist meist der Fall, wenn man die Web Components (z. B. als Designelemente) mehrfach verwenden will oder eine Isolation von Bereichen innerhalb einer Seite benötigt. Ist das der Fall, muss man sich bewusstmachen, welche Konsequenzen man sich damit einhandelt. Die Web Components sind dann nämlich eine gesharte Library von mehreren Konsumenten. Zusätzlich gibt es die Komplexität, dass eigentlich alle Konsumenten möglichst die gleiche Version verwenden sollen. Ist die Entscheidung, Web Components geshart einzusetzen, bewusst gefallen, ist es am einfachsten, wenn man das API so klein wie möglich hält, um Probleme in der Wartung von vornherein zu vermeiden. Das beste API ist „kein API“. Gerade bei Formularelementen bietet es sich an, ein Wrapper-Element um ein <input/> zu bauen, das das Standardelement mit Funktionalität und Design anreichert. Die Webspezifikationen geben schon viel her und man muss nicht alles nochmal erfinden. Indem man gezielt standardisierte HTML-Elemente wiederverwendet, kann man sein eigenes spezielles API kleiner halten. Um sowohl das Frontend-Design und Styling als auch das Thema API-Design adäquat zu berücksichtigen, ist es sinnvoll, beide Qualitäten auch im Team vertreten zu haben.

Wenn aber eine Komponente sowieso nicht von mehreren Konsumenten wiederverwendet werden soll, dann sind Web Components wahrscheinlich auch wenig sinnvoll, und man braucht keine neue Technologie zu lernen.

Geschrieben von
Hendrik Müller
Hendrik Müller
Hendrik Müller ist Enterprise Developer bei der OPEN KNOWLEDGE GmbH in Oldenburg. Mit dem Schwerpunkt auf Webtechnologien begleiten ihn momentan Angular und Web Components durch den Tag. Abseits davon beschäftigt er sich leidenschaftlich mit dem Design von Programmiersprachen.
Kommentare

Hinterlasse einen Kommentar

avatar
4000
  Subscribe  
Benachrichtige mich zu: