Anzeige
Anzeige
How-To

SkateJs: Die Bibliothek für Web-Komponenten im Praxiseinsatz

Wiederverwendbare Web Components sparen Entwicklern jede Menge Arbeit und Zeit. Wer selbst eine Komponente entwickeln will, kann mittlerweile auf hilfreiche ­Bibliotheken zurückgreifen. Wie das genau funktioniert und was es zu beachten gilt, zeigt die Arbeit mit SkateJS.

Von Aaron Czichon
6 Min.
Artikel merken
Anzeige
Anzeige

(Abbildung: Shutterstock/ Elle Aon)

Wer Entwicklungszeit einsparen kann, der lässt sich das in der Regel nicht zweimal sagen – Web Components helfen dabei: Die wiederverwendbaren Komponenten lassen sich ohne zusätzlichen Aufwand über Custom-Elements an verschiedenen Stellen in Webprojekten einsetzen. Seit 2018 sind die Web-­Components-Standards in nahezu allen Browsern implementiert. Dank ­Polyfills, also Libraries, die neue, eventuell noch nicht unter­stützte Funktionalitäten nachliefern, lassen sie sich auch in ­älteren Browsern nutzen.

Anzeige
Anzeige

Mit der Web-Components-Bibliothek SkateJS können Entwickler entsprechende Komponenten erstellen. Die Library erfreut sich immer größerer Beliebtheit: In den vergangenen ­Monaten haben sich die Downloadzahlen von ­SkateJS von 60.000 fast auf 120.000 pro Monat verdoppelt. Wie aber funktioniert das Erstellen von Webkomponenten mit SkateJS genau? Das soll das folgende Beispiel zeigen, bei dem eine Komponente mit SkateJS und LitHTML entwickelt wird, die die Wetterdaten zu einem bestimmten Ort ausgibt.

Web Components mit SkateJS und LitHTML

SkateJS abstrahiert einige der nativen Schnittstellen. Im Prinzip betrifft das bei SkateJS vor allem das Rendering und die Entwicklung der Komponentenklasse. Das eigentliche Benutzen einer Webkomponente erfolgt dann mit nativen Bordmitteln.

Anzeige
Anzeige

SkateJS-Komponenten lassen sich in drei große Bestandteile gliedern: Für das Rendering im DOM ist ein Renderer erforderlich, der mit SkateJS kompatibel ist. Dazu zählen Out-of-the-Box LitHTML, Preact und React. SkateJS bietet auch die Option, einen eigenen Renderer zu benutzen. Das macht die Library letztlich mit nahezu jedem Renderer kompatibel.

Anzeige
Anzeige
Im Gegensatz zu vielen anderen Web-Component-Librarys arbeitet SkateJS mit den nativen Web-Component-API. Sie gehören ­zusammen mit Mixins und dem Renderer zum Grundaufbau von SkateJS-Components. (Abbildung: Aaron Czichon/t3n)

Im Gegensatz zu vielen anderen Web-Component-Librarys arbeitet SkateJS mit den nativen Web-Component-API. Sie gehören ­zusammen mit Mixins und dem Renderer zum Grundaufbau von SkateJS-Components. (Abbildung: Aaron Czichon/t3n)

Die zweite Komponentenvariante sind die sogenannten Mixins: abstrakte Sub-Klassen, die für die Definition eigener Sub-Klassen genutzt werden können. Die Mixins von SkateJS definieren verschiedene Lifecycle-Hooks einer Komponente. Die verwendeten Mixins sind davon abhängig, wie die Komponenten genutzt werden: ob mit einem Renderer, als Component oder mit einem Kontext.

Der dritte Teil sind die nativen Web-Component-API. Im Vergleich zu anderen Web-Component-Bibliotheken basiert SkateJS auf der nativen Implementierung der Webkomponenten. Somit sind alle API der Web-Component-Spezifikation ohne weiteres auch mit Skate zu nutzen.

Anzeige
Anzeige

Erstellen einer SkateJS-Umgebung: So klappt es

Für ein neues Projekt mit SkateJS wird zunächst ein NPM-Projekt initialisiert (npm init -y). Für den lokalen Build-Prozess verwenden wir Babel für die Transpilierung und Webpack für das Packaging:

$ npm install babel-core babel-loader babeö-preset-stage-0 webpack webpack-cli --save-dev

Wenn nun die Tools für den Build installiert sind, benötigen wir die Libraries, die wir zur Entwicklung brauchen. In diesem ­Beispiel verwenden wir LitHTML, eine kleine, effiziente ­Javascript-Library, als Renderer:

$ npm install skatejs lit-html @skatejs/renderer-lit-html @webcomponents/webcomponentjs --save

Um das Setup zu komplettieren, hinterlegen wir jetzt in der ­­­package.json noch die Scripts für den Build und den Watch-­Prozess.

Anzeige
Anzeige
„scripts“: {
  „build“: „webpack --mode production“,
  „start“: „webpack --mode development --watch“
}

Außerdem benötigen wir die Konfigurationen für Babel und ­Webpack.

// .babelrc
{
  „presets“: [„stage-0“]
}
```
```
// webpack.config.js
module.exports = {
    module: {
       rules: [
    {
      use: „babel-loader“
    }
      ]
    }
};

Die erste Webkomponente definieren

Damit wir eine Webkomponente schreiben können, die auch mit Events arbeiten kann, benötigen wir ein paar Hilfefunktionen in Form der Mixins. Das nachfolgende Mixin beschreibt ein normales HTML-Element, das um selbst definierte Events erweitert wird. Da ein normales HTML-Element nur Events kennt, die vom W3C festgelegt und in die Browser implementiert worden sind, müssen wir dieses Interface manuell erweitern.

// src/util.js
import { emit, withComponent } from „skatejs/dist/esnext“;
import withRenderer from „@skatejs/renderer-lit-html/dist/esnext“;
// Mixin for auto-defining functions that emit events.
const withEvents = (Base = HTMLElement) =>
  class extends Base {
    static get observedAttributes() {
      (this.events || []).forEach(e => {
        const name = `on${e[0].toUpperCase() + e.substring(1)}`;
        Object.defineProperty(this.prototype, name, {
          get() {
            if (!this.__events[e]) {
              this[name] = () => {};
            }
            return this.__events[e];
          },
          set(fn) {
            this.__events[e] = detail => {
              fn(detail);
              emit(this, e, { detail });
            };
            if (this.triggerUpdate) {
              this.triggerUpdate();
            }
          }
        });
      });
      return super.observedAttributes;
    }
    __events = {};
  };
export const Component = withEvents(withComponent(withRenderer()));

Für eine neue Webkomponente wird zunächst ein Use-Case definiert. In diesem Fall entwickeln wir eine Komponente, die Wetterdaten von OpenWeatherMap abruft und anzeigt. Die Komponente soll später mit dem Tag weather-component genutzt werden können. Per Attribut soll die Stadt angegeben werden, zu der die Wetterdaten gehören.

Anzeige
Anzeige

Zunächst wird eine neue, leere Komponente definiert.

// src/weather-component.js
import { props } from „skatejs/dist/esnext“;
import { html } from „lit-html/lib/lit-extended“;
import { Component } from „./util“;
export default class extends Component {
    static state = {
        city: {}
    }
    updateState(state) {
        this.state = state;
    }
    render() {
        return html`
        &ltdiv>Ich bin die Weather Component&lt/div>
        `;
    }
}

Es wird eine neue Klasse erstellt, die von der Basisklasse erbt, die zuvor als Mixin angelegt wurde. Ebenso wird das HTML-­Objekt importiert, das den Renderer von LitHTML repräsentiert. Die Render-Funktion ist eine Standardfunktion, die als Rück­gabe immer ein TemplateResult hat, sprich: das entsprechende ­Template, das im DOM gerendert werden soll.

Um den aktuellen Stand der Komponente zu betrachten, ergänzen wir das Projekt noch um eine index.html, die die Komponente nutzt.

Anzeige
Anzeige
< !-- index.html -->
< script src=“./dist/main.js“>

Um die Nutzung komplett zu machen, wird ein Einstiegsskript benötigt, das die Komponentenklasse als Custom-Element ­definiert.

// src/index.js
import ‚@webcomponents/webcomponentsjs‘;
import WeatherComponent from „./weather-component“;
customElements.define(„weather-component“, WeatherComponent);

Mit einem einfachen npm start lässt sich ein Development-­Server starten, der dann die Komponente im Browser darstellt.

Über die API landen die Wetterdaten in der Browser-Konsole und können dann im Frontend dargestellt werden. (Screenshot: Aaron Czichon)

Über die API landen die Wetterdaten in der Browser-Konsole und können dann im Frontend dargestellt werden. (Screenshot: Aaron Czichon)

Nachdem die Grundlagen für eine Web Component gelegt sind, sollen nun echte Daten benutzt werden. Zunächst muss die API von OpenWeatherMap angebunden werden. Um ohne zusätzliche JavaScript-Packages auszukommen, können wir hierzu die Fetch-API nutzen. Die Web-Component-Spezifikation sieht vor, eine sogenannte connectedCallback-Funktion aufzurufen, sobald eine Komponente an ein DOM-Element angehängt wird. Diese ­Lifecycle-Funktion kann nun genutzt werden, um die Daten von der Wetter-API aufzurufen.

Anzeige
Anzeige
// src/weather-component.js
connectedCallback() {
        fetch(‚https://api.openweathermap.org/data/2.5/weather?q=Heidenheim&APPID=API_TOKEN‘)
            .then(res => res.json())
            .then(data => console.log(data));
 }

Wird nun die Anwendung im Browser erneut geladen, finden wir die Wetterdaten der OpenWeatherMap in der Browser-Konsole (siehe Abbildung links).

Da die Komponente später in unterschiedlichen Applikationen genutzt werden soll, darf der Ort für die Wetterdaten nicht festgelegt sein. Daher können wir Propertys (Attribute) für eine Komponente definieren, die von außen in die Komponente gegeben werden können.

Die Definition von Propertys innerhalb einer Komponente erfolgt bei SkateJS mithilfe von props, die aus skatejs importiert werden. Dabei muss ein Property immer mit einem Inputtyp definiert werden.

Anzeige
Anzeige

Natürlich soll das Property city in dem Aufruf von der API genutzt werden. Dazu muss die connectedCallback-Funktion etwas angepasst werden. Zusätzlich kann man den API-Token entsprechend auch als Property definieren.

// src/weather-component.js

static props = {
    city: props.string,
    token: props.string
}
connectedCallback() {
    fetch(`https://api.openweathermap.org/data/2.5/weather?q=${this.props.city}&APPID=${this.props.token}`)
        .then(res => res.json())
        .then(data => console.log(data));
}

Auch das entsprechende Test-Markup gilt es anzupassen:

<!-- index.html -->
<weather-component city="Heidenheim" token="YOUR-TOKEN"></weather-component>

Um die Komponente nun zu komplettieren, wird das Ergebnis des API-Aufrufs im State gespeichert und im Markup ausgegeben.

Um den State zu erweitern, werden entsprechende Propertys definiert, die die nötigen Informationen enthalten. Diese werden dann mit dem Ergebnis des API-Aufrufes aus dem connectedCallback festgelegt.

Um den State zu aktualisieren, wird immer das ­komplette State-­Objekt neu festgelegt. Der Renderer, in diesem Fall ­LitHTML, erkennt Property-Aktualisierungen innerhalb eines Objektes nicht automatisch. Um daher das erneute Zeichnen des DOM (Re-Rendering) zu starten, wird das State-Objekt neu ­gesetzt.

Innerhalb der Render-Funktion wird dann auf die entsprechenden Propertys des States zurückgegriffen und sie werden in den Rendering-Prozess eingebunden.

// src/weather-component.js
export default class extends Component {
    static state = {
        temp: 0,
        city: ‚‘
    }
...
    render() {
        return html`
        <div>Currently the weather in ${this.state.city} is about ${this.state.temp} Celsius.</div>
        `;
    }
}

Der Browser rendert nun im DOM das gewünschte Ergebnis:

Currently the weather in Heidenheim is about 5.11 Celsius.

Fazit

SkateJS bietet eine sehr geringe Abstraktionsebene für die ­bestehenden Web-Component-API und erweitert deren Möglichkeiten, für das DOM-Rendering verschiedene Renderer wie Preact, React oder LitHTML zu nutzen. Neben SkateJS gibt es natürlich zahlreiche weitere Frameworks und Libraries, die das Entwickeln von Webkomponenten vereinfachen sollen: Etwa Polymer, das die Ansätze der Web Components als erstes aufgegriffen und umgesetzt hat oder Angular Element, das es ermöglicht, bestehende Angular-Komponenten mit standardisierten Web Components kompatibel zu machen. Der Web-Component-­Compiler StencilJS wiederum greift die Konzepte von Angular und React auf und exportiert mithilfe von Typescript standardisierte Webkomponenten. Entwickler sollten ruhig verschiedene Tools ausprobieren – die hierfür benötigte Zeit wird dann später beim komponentenbasierten Entwickeln wieder eingespart.

Mehr zu diesem Thema
Fast fertig!

Bitte klicke auf den Link in der Bestätigungsmail, um deine Anmeldung abzuschließen.

Du willst noch weitere Infos zum Newsletter? Jetzt mehr erfahren

Anzeige
Anzeige
Schreib den ersten Kommentar!
Bitte beachte unsere Community-Richtlinien

Wir freuen uns über kontroverse Diskussionen, die gerne auch mal hitzig geführt werden dürfen. Beleidigende, grob anstößige, rassistische und strafrechtlich relevante Äußerungen und Beiträge tolerieren wir nicht. Bitte achte darauf, dass du keine Texte veröffentlichst, für die du keine ausdrückliche Erlaubnis des Urhebers hast. Ebenfalls nicht erlaubt ist der Missbrauch der Webangebote unter t3n.de als Werbeplattform. Die Nennung von Produktnamen, Herstellern, Dienstleistern und Websites ist nur dann zulässig, wenn damit nicht vorrangig der Zweck der Werbung verfolgt wird. Wir behalten uns vor, Beiträge, die diese Regeln verletzen, zu löschen und Accounts zeitweilig oder auf Dauer zu sperren.

Trotz all dieser notwendigen Regeln: Diskutiere kontrovers, sage anderen deine Meinung, trage mit weiterführenden Informationen zum Wissensaustausch bei, aber bleibe dabei fair und respektiere die Meinung anderer. Wir wünschen Dir viel Spaß mit den Webangeboten von t3n und freuen uns auf spannende Beiträge.

Dein t3n-Team

Melde dich mit deinem t3n Account an oder fülle die unteren Felder aus.

Bitte schalte deinen Adblocker für t3n.de aus!
Hallo und herzlich willkommen bei t3n!

Bitte schalte deinen Adblocker für t3n.de aus, um diesen Artikel zu lesen.

Wir sind ein unabhängiger Publisher mit einem Team von mehr als 75 fantastischen Menschen, aber ohne riesigen Konzern im Rücken. Banner und ähnliche Werbemittel sind für unsere Finanzierung sehr wichtig.

Schon jetzt und im Namen der gesamten t3n-Crew: vielen Dank für deine Unterstützung! 🙌

Deine t3n-Crew

Anleitung zur Deaktivierung
Artikel merken

Bitte melde dich an, um diesen Artikel in deiner persönlichen Merkliste auf t3n zu speichern.

Jetzt registrieren und merken

Du hast schon einen t3n-Account? Hier anmelden

oder
Auf Mastodon teilen

Gib die URL deiner Mastodon-Instanz ein, um den Artikel zu teilen.

Anzeige
Anzeige