Webkomponenten mit Stencil – Ein erster Überblick

10/09/2017

Dieser Artikel gibt eine Einführung in die Entwicklung von Webkomponenten mit Hilfe von Stencil. Stencil ist ein Kompiler für plattformunabhängige Webkomponenten, der zur Zeit vom Team hinter dem Ionic-Framework entwickelt wird und erst kürzlich im August auf dem Polymer Summit 2017 zum ersten Mal der Öffentlichkeit vorgestellt wurde.

Webkomponenten sind eine Erweiterung der HTML- und DOM-Spezifikation, die aktuell durch das Word Wide Web-Konsortium (W3C) vorgenommen wird. Es handelt sich dabei um in sich gekapselte, wiederverwendbare Widgets und Komponenten, die in Webseiten und Webapplikationen eingebunden werden können. Webkomponenten sind auch ein elementarer Bestandteil von Progressiven Web Apps (PWA), auf die wir in einem früheren Beitrag eingegangen sind.

Hier soll nun eine erste deutschsprachige Einführung in Stencil gegeben werden. Er wird erläutert, um was es sich bei Stencil handelt und wie mit dieser noch sehr neuen Technologie eigene Webkomponenten erzeugt werden können. Links zu weiterführenden Informationen rund um Stencil bzw. Webkomponenten finden sich am Ende dieses Artikels.

Kurz als Warnung: Stencil ist schon jetzt sehr rund und zumindest beim Ionic-Team auch schon im professionellen Einsatz. Aber die Software befindet sich in einem frühen Stadium und ist noch immer in der Entwicklung. Dennoch lohnt sich schon jetzt ein Blick auf diese möglicherweise zukunftsweisende Technologie.

Die folgende Kurzanleitung setzt Erfahrung mit Javascript, Git und Node.js bzw. NPM voraus. Darüber hinaus sind Grundkenntnisse in Angular, React, TypeScript und SASS hilfreich. Hier wird das Nötigste dazu kurz erklärt, kann aber in einem kurzen Artikel natürlich nicht komplett abgedeckt werden.

Inhalt

Was ist Stencil?

Stencil ist ein Kompiler, mit dessen Hilfe plattformunabhängige Webkomponenten erstellt werden können. Konkret werden mit Hilfe von Stencil benutzerdefinierte Elemente (Custom Elements) erzeugt, wobei der erzeugte Code aus nichts weiter als Vanilla-JavaScript, HTML und CSS besteht und daher in jedem modernen Browser ausführbar ist.

Webkomponenten gemäß der V1-Spefizikation werden von Haus aus in den Browsern Chrome und Safari dargestellt, während für Edge und Firefox an der Unterstützung gearbeitet wird. Stencil verwendet jedoch einen dynamischen Lader, über den mit Hilfe sogenannter Polyfills schon heute weitaus mehr Browser die von Stencil erzeugte Webkomponenten unterstützen: zusätzlich zu Chrome und Safari auch Firefox, Edge und sogar der Internet Explorer 11. Der aktuelle Stand findet sich auch auf caniuse.com.

Stencil ist also weder ein neues Framework, noch eine Bibliothek. Vielmehr können die mit Stencil erzeugten Webkomponenten in beliebigen anderen Frameworks genutzt oder auch einfach direkt in den HTML-DOM eingebunden werden. Das macht sie zu flexiblen, wiederverwendbaren Bausteinen moderner Webseiten und Webanwendungen wie PWAs.

Bevor wir uns gleich eine Beispiel-Webkomponente mit Hilfe von Stencil erzeugen, eine kurze Übersicht zu den Eigenschaften von Stencil-Webkomponenten und ihre Vorteile:

Eigenschaften von mit Stencil erzeugten Webkomponenten

Stencil vereint in sich einige bewährte sowie einige sehr neue Konzepte anderer Frameworks wie Angular oder React:

  • Virtueller DOM – eine Repräsentation des eigentlichen DOMs, über die eine schnellere Bearbeitung und Aktualisierung desselben erfolgen kann; ähnlich wie es die Frameworks Vue.js und React umsetzen.
  • Lazy Loading – die Komponenten werden asynchron und damit deutlich schneller geladen und können mit anderen weiteren Komponenten kombiniert werden.
  • Asynchrones Rendering – ähnlich wie in der Entwicklerversion von React („Fiber“) gelöst.
  • Reaktives Data-Binding – reaktive Bindung zwischen den Daten im DOM und in der Komponente.
  • TypeScript – wie schon Angular oder Ionic unterstützt auch Stencil im Quellcode TypeScript, die Erweiterung des neuen ECMAScript-Sprachkerns u.a um Klassen, Interfaces und statische Typisierung.
  • JSX – eine einbettbare XML-artige Syntax, aus der JavaScript-Code erzeugt wird; als Teil von React entwickelt wird JSX nun auch von TypeScript unterstützt.

Vorteile von mit Stencil erzeugten Webkomponenten

Mit Stencil erzeugte Webkomponenten weisen einige Vorteile gegenüber herkömmlichen Ansätzen auf:

  • Performance – die Komponenten sind im Vergleich zu Lösungen mittels Frameworks ohne unnötigen Ballast und dementsprechend schnell.
  • Stabilität – der Code besteht aus bewährtem Standard-Javascript/HTML/CSS und unterliegt daher nicht den permanenten Versionsänderungen moderner Frameworks.
  • Interoperabilität – die Komponenten lassen sich in allen größeren Frameworks verwenden.
  • Vertrautheit – viele bewährte Elemente anderer Frameworks sind in kompakter Form auch in Stencil realisiert worden.

Die Beispiel-Webkomponente

Das Stencil-Team stellt auf GitHub eine einfache Beispiel-Webkomponente zur Verfügung, die mittels Git einfach heruntergeladen werden kann. Über NPM werden dann alle erforderlichen Module nachgeladen und installiert:

$ git clone https://github.com/ionic-team/stencil-starter.git my-app
$ cd my-app
$ git remote rm origin
$ npm install

Der Quellcode der Webkomponente befindet sich im Unterordner src. Dieser wird mit dem Befehl npm start kompiliert und die erzeugte Webkomponente im Unterordner www abgelegt:

$ npm start

> @stencil/starter@0.0.1 start /home/onno/Development/Stencil/my-app
> npm run dev
> @stencil/starter@0.0.1 dev /home/onno/Development/Stencil/my-app
> sd concurrent "stencil build --dev --watch" "stencil-dev-server"

[34:13.0]  build, dev mode, started ...
[34:13.0]  compile started ...
[34:15.2]  compile finished in 2.18 s
[34:15.2]  bundle styles started ...
[34:15.2]  bundle modules started ...
[34:15.3]  bundle styles finished in 146 ms
[34:15.4]  bundle modules finished in 220 ms
[34:15.4]  build finished, watching for changes... in 2.45 s

Unsere erste Stencil-Webkomponente ist damit schon fertig. Schauen wir sie uns an!

Um die Komponente mit einem Browser anzuschauen, muss der Inhalt des www-Ordners samt der index.html-Datei durch einen Server ausgeliefert werden. Dazu kann der Inhalt des www-Ordners einfach auf einen Server übertragen werden. Noch einfacher ist es mit dem kleinen Webserver, den Stencil gleich mitliefert und der automatisch nach dem obigen Kompilierungsprozess gestartet wird. Dieser liefert über die URL http://localhost:3333/ die Webkomponente an einen Browser aus und erneuert die Ausgabe auch automatisch nach jeder Änderung am Code.

Die Ausgabe der Beispiel-Webkomponente in zunächst wenig spektakulär:

Die Ausgabe der Beispiel-Webkomponente.

Dahinter verbirgt sich aber ein höchst flexibler Mechanismus, mit dessen Hilfe auch deutlich komplexere Darstellungen komfortabel umgesetzt werden können. Im Folgenden werfen wir einen Blick auf den sich hinter der Ausgabe verbergenden Quellcode.

Ein Blick in den Quellcode

Schauen wir als erstes auf die Datei index.html im src-Ordner. Diese enthält eine ganz normale einfache HTML-Webseite:

<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0,
        minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <title>Stencil Starter App</title>
  <script src="build/app.js"></script>
</head>
<body>

  <my-name first="Stencil" last="JS"></my-name>

</body>
</html>

Oben im Header wird die Webkomponente app.js geladen, welche durch den Stencil-Kompiler erzeugt wurde. Diese befindet sich natürlich nicht im src-Ordner, sondern wurde von Stencil unter www/build/ abgelegt. Beim Erzeugen der Webkomponente wurde auch die index.html-Datei aus dem src-Ordner in den www-Ordner kopiert.

Die Webkomponente wird in der index.html ganz einfach durch das dazu gehörige Custom Element my-name eingebunden:

  <my-name first="Stencil" last="JS"></my-name>

Hier werden auch gleich zwei Input-Parameter für die Webkomponente gesetzt: first und last, welche durch die Komponente ausgewertet werden. Die dazu gehörige Programmlogik der Webkomponente steckt in der Datei my-name.tsx, welche sich im Ordner src/my-name/ befindet. Die Datei trägt die Endung .tsx, da sie TypeScript- und JSX-Code enthält:

import { Component, Prop } from '@stencil/core';

@Component({
  tag: 'my-name',
  styleUrl: 'my-name.scss'
})
export class MyName {

  @Prop() first: string;
  @Prop() last: string;

  render() {
    return (
      <p>
        Hello, my name is {this.first} {this.last}
      </p>
    );
  }
}

In der ersten Zeile werden jene Teile aus dem Paket @stencil/code importiert, die wir für die Datei benötigen. Das ist zunächst der @Component()-Dekorator. Dekoratoren (engl. decorators) wurden mit TypeScript bzw. ECMAScript6 eingeführt. Mit ihrer Hilfe kann einer Klasse zusätzliche Meta-Angaben hinzugefügt werden. Hier ist dies der Name des HTML-Elements der Komponente (tag) sowie die für die Optik der Komponente zuständige SASS-Datei (styleURL). Aus der SASS-Datei erzeugt der Kompiler dann die CSS-Datei.

Es folgt die Definition der Klasse MyName, welche mit dem Setzen zweier @Prop()-Dekoratoren beginnt. Mit diesen werden der Klasse zwei Argumente first und last zur verfügung gestellt, die öffentlich (public) sind und deren Werte im Custom Element gesetzt werden. Da dies in TypeScript geschieht, wird den Argumenten auch der Typ (hier: string) mitgegeben.

Schließlich wird in der render()-Funktion die Ausgabe der Webkomponente definiert. In diesem Fall ist dies ein Absatz mit einem einzigen Satz, bei dem die beiden Argumente first und last eingesetzt werden. Die Funktion render() enthält eine deklarative Template-Syntax wie sie mit JSX eingeführt wurde. Die mit @Props() definierten Eigenschaften werden von der Komponente überwacht und bei jeder Änderung der Werte wird die render()-Funktion erneut aufgerufen.

Die Ausgabe kann auch Entscheidungen, Schleifen und weitere Eingaben enthalten. Einen Überblick gibt die Stencil-Webseite. Auf der Webseite werden auch die weiteren Elemente der Stencil-API erläutert:

  • @Component() – wie oben erwähnt, werden damit der tag des Custom Elements sowie das zu verwendende style sheet festgelegt.
  • @Prop() – erzeugt eine Eigenschaft der Komponente, die vom Custom Element aus gesetzt wird ist. Änderungen bewirken ein erneutes Rendern der Komponente.
  • @State() – erzeugt einen internen Zustand der Komponente, der nicht von außen zugänglich ist. Änderungen bewirken ein erneutes Rendern der Komponente.
  • @Method() – wird verwendet, um eine Methode der Komponente zu veröffentlichen, d.h. dem Custom Element zugänglich zu machen.
  • @Event() – erzeugt benutzerdefinierte DOM-Events, um Daten und Events an andere Komponenten zu senden.
  • @Listen() – Hört (listen) auf Events, die von Kinderkomponenten ausgedendet werden.
  • @Element() – gibt eine Instant eines HTML-Elementes zurück, um es der Komponente zugänglich zu machen.

Darüber hinaus verwendet Stencil weitere Konzepte wie Hooks, die zu definierten Punkten im Lebenszyklus einer Komponente aufgerufen werden, serverseitiges Rendering oder auch einen eigenen Router, über den Webkomponente über festgelegte URL-Pfade gestartet werden können.

Anwendungsbeispiele für Stencil-Webkomponenten

  • Eine StencilNews-PWA bestehend aus Stencil-Webkomponenten.
  • Die Stencil-Webseite besteht ebenfalls komplett aus Stencil-Webkomponenten und ist im Quellcode auch auf GitHub veröffentlicht.
  • Eine persönliche Webseite als PWA mit Stencil-Webkomponenten von Fer. D. Olmo.

Weitere Informationen

Zum Thema Webkomponenten:

Zu Stencil: