1. Einführung
Letzte Aktualisierung:10.08.2021
Web Components
Webkomponenten sind eine Reihe von Webplattform-APIs, mit denen Sie neue benutzerdefinierte, wiederverwendbare, gekapselte HTML-Tags erstellen können, die Sie auf Webseiten und in Web-Apps verwenden können. Benutzerdefinierte Komponenten und Widgets, die auf den Webkomponentenstandards basieren, funktionieren in allen modernen Browsern und können mit jeder JavaScript-Bibliothek oder jedem JavaScript-Framework verwendet werden, das mit HTML kompatibel ist.
Was ist Lit?
Lit ist eine einfache Bibliothek zum Erstellen schneller, schlanker Webkomponenten, die in jedem Framework oder ohne Framework funktionieren. Mit Lit können Sie wiederverwendbare Komponenten, Anwendungen, Designsysteme und mehr erstellen.
Lit bietet APIs, um gängige Webkomponenten-Aufgaben wie das Verwalten von Attributen und das Rendern zu vereinfachen.
Lerninhalte
- Was ist eine Webkomponente?
- Konzepte von Webkomponenten
- Webkomponente erstellen
- Was sind lit-html und LitElement?
- Was Lit zusätzlich zu einer Webkomponente bietet
Umfang
- Eine reine Webkomponente für „Mag ich“- und „Mag ich nicht“-Bewertungen
- Eine Lit-basierte Webkomponente mit „Mag ich“- und „Mag ich nicht“-Symbolen
Voraussetzungen
- Ein beliebiger aktueller moderner Browser (Chrome, Safari, Firefox, Chromium Edge). Webkomponenten funktionieren in allen modernen Browsern. Für Microsoft Internet Explorer 11 und nicht auf Chromium basierende Versionen von Microsoft Edge sind Polyfills verfügbar.
- Kenntnisse von HTML, CSS, JavaScript und den Chrome-Entwicklertools
2. Einrichtung und Nutzung des Playgrounds
Auf den Code zugreifen
Im gesamten Codelab finden Sie Links zum Lit-Playground, z. B.:
Die Playground-Umgebung ist eine Code-Sandbox, die vollständig in Ihrem Browser ausgeführt wird. Es kann TypeScript- und JavaScript-Dateien kompilieren und ausführen und auch automatisch Importe in Knotenmodule auflösen, z. B.
// before
import './my-file.js';
import 'lit';
// after
import './my-file.js';
import 'https://unpkg.com/lit?module';
Sie können das gesamte Tutorial im Lit-Playground durcharbeiten und diese Prüfpunkte als Ausgangspunkt verwenden. Wenn Sie VS Code verwenden, können Sie diese Prüfpunkte nutzen, um den Startcode für einen beliebigen Schritt herunterzuladen und Ihre Arbeit zu überprüfen.
Benutzeroberfläche des beleuchteten Spielplatzes

Der Screenshot der Lit-Playground-Benutzeroberfläche zeigt die Abschnitte, die Sie in diesem Codelab verwenden werden.
- Dateiauswahl Beachten Sie das Pluszeichen…
- Dateieditor
- Codevorschau
- Schaltfläche „Aktualisieren“
- Grafik: Symbol zum Herunterladen
VS Code-Einrichtung (Advanced)
Diese VS Code-Einrichtung bietet folgende Vorteile:
- Vorlagentyp prüfen
- IntelliSense und automatische Vervollständigung für Vorlagen
Wenn Sie NPM und VS Code (mit dem lit-plugin-Plug-in) bereits installiert haben und wissen, wie Sie diese Umgebung verwenden, können Sie diese Projekte einfach herunterladen und starten. Gehen Sie dazu so vor:
- Drücken Sie die Schaltfläche zum Herunterladen.
- Inhalt der TAR-Datei in ein Verzeichnis extrahieren
- Installieren Sie einen Entwicklungsserver, der Bare-Modulspezifizierer auflösen kann. Das Lit-Team empfiehlt @web/dev-server.
- Hier ein Beispiel:
package.json
- Hier ein Beispiel:
- Führen Sie den Entwicklungsserver aus und öffnen Sie Ihren Browser. Wenn Sie
@web/dev-serververwenden, können Sienpx web-dev-server --node-resolve --watch --openverwenden.- Wenn Sie das Beispiel
package.jsonverwenden, nutzen Sienpm run serve.
- Wenn Sie das Beispiel
3. Benutzerdefiniertes Element definieren
Benutzerdefinierte Elemente
Web Components sind eine Sammlung von vier nativen Web-APIs. Diese sind:
- ES-Module
- Benutzerdefinierte Elemente
- Shadow DOM
- HTML-Vorlagen
Sie haben bereits die ES-Modulspezifikation verwendet, mit der Sie JavaScript-Module mit Importen und Exporten erstellen können, die mit <script type="module"> auf die Seite geladen werden.
Benutzerdefiniertes Element definieren
Mit der Spezifikation für benutzerdefinierte Elemente können Nutzer eigene HTML-Elemente mit JavaScript definieren. Die Namen müssen einen Bindestrich (-) enthalten, um sie von nativen Browserelementen zu unterscheiden. Löschen Sie die Datei index.js und definieren Sie eine benutzerdefinierte Elementklasse:
index.js
class RatingElement extends HTMLElement {}
customElements.define('rating-element', RatingElement);
Ein benutzerdefiniertes Element wird definiert, indem eine Klasse, die HTMLElement erweitert, mit einem Tag-Namen mit Bindestrich verknüpft wird. Der Aufruf von customElements.define weist den Browser an, die Klasse RatingElement mit dem tagName ‘rating-element' zu verknüpfen. Das bedeutet, dass jedes Element in Ihrem Dokument mit dem Namen <rating-element> mit dieser Klasse verknüpft wird.
Fügen Sie ein <rating-element> in den Dokumenttext ein und sehen Sie sich an, was gerendert wird.
index.html
<body>
<rating-element></rating-element>
</body>
Wenn Sie sich die Ausgabe ansehen, werden Sie feststellen, dass nichts gerendert wurde. Das ist zu erwarten, da Sie dem Browser nicht mitgeteilt haben, wie <rating-element> gerendert werden soll. Sie können bestätigen, dass die Definition des benutzerdefinierten Elements erfolgreich war, indem Sie in der Elementauswahl der Chrome-Entwicklertools <rating-element> auswählen und in der Konsole Folgendes aufrufen:
$0.constructor
Die Ausgabe sollte so aussehen:
class RatingElement extends HTMLElement {}
Lebenszyklus benutzerdefinierter Elemente
Benutzerdefinierte Elemente haben eine Reihe von Lebenszyklus-Hooks. Diese sind:
constructorconnectedCallbackdisconnectedCallbackattributeChangedCallbackadoptedCallback
constructor wird aufgerufen, wenn das Element zum ersten Mal erstellt wird, z. B. durch Aufrufen von document.createElement(‘rating-element') oder new RatingElement(). Der Konstruktor ist ein guter Ort, um das Element einzurichten. Es gilt jedoch in der Regel als schlechte Praxis, DOM-Manipulationen im Konstruktor auszuführen, da dies die Leistung beim „Booten“ des Elements beeinträchtigen kann.
connectedCallback wird aufgerufen, wenn das benutzerdefinierte Element an das DOM angehängt wird. Hier finden in der Regel die ersten DOM-Manipulationen statt.
Die disconnectedCallback wird aufgerufen, nachdem das benutzerdefinierte Element aus dem DOM entfernt wurde.
attributeChangedCallback(attrName, oldValue, newValue) wird aufgerufen, wenn sich eines der vom Nutzer angegebenen Attribute ändert.
Die adoptedCallback wird aufgerufen, wenn das benutzerdefinierte Element über adoptNode wie in HTMLTemplateElement von einem anderen documentFragment in das Hauptdokument übernommen wird.
DOM rendern
Kehren Sie nun zum benutzerdefinierten Element zurück und verknüpfen Sie es mit einem DOM. Legen Sie den Inhalt des Elements fest, wenn es an das DOM angehängt wird:
index.js
class RatingElement extends HTMLElement {
constructor() {
super();
this.rating = 0;
}
connectedCallback() {
this.innerHTML = `
<style>
rating-element {
display: inline-flex;
align-items: center;
}
rating-element button {
background: transparent;
border: none;
cursor: pointer;
}
</style>
<button class="thumb_down" >
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating">${this.rating}</span>
<button class="thumb_up">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>
`;
}
}
customElements.define('rating-element', RatingElement);
Im constructor-Element speichern Sie eine Instanzeigenschaft mit dem Namen rating. Im connectedCallback fügen Sie <rating-element> untergeordnete DOM-Elemente hinzu, um die aktuelle Bewertung zusammen mit den Schaltflächen „Mag ich“ und „Mag ich nicht“ anzuzeigen.
4. Shadow DOM
Warum Shadow DOM?
Im vorherigen Schritt haben Sie gesehen, dass mit den Selektoren im eingefügten Style-Tag alle Bewertungselemente auf der Seite sowie alle Schaltflächen ausgewählt werden. Das kann dazu führen, dass die Formatierungen aus dem Element herausgelangen und andere Knoten ausgewählt werden, die Sie möglicherweise nicht formatieren möchten. Außerdem können andere Stile außerhalb dieses benutzerdefinierten Elements unbeabsichtigt die Knoten innerhalb des benutzerdefinierten Elements formatieren. Fügen Sie beispielsweise ein Style-Tag in den Head des Hauptdokuments ein:
index.html
<!DOCTYPE html>
<html>
<head>
<script src="./index.js" type="module"></script>
<style>
span {
border: 1px solid red;
}
</style>
</head>
<body>
<rating-element></rating-element>
</body>
</html>
Die Ausgabe sollte einen rot umrandeten Kasten um den Bereich für die Bewertung enthalten. Dies ist ein trivialer Fall, aber das Fehlen der DOM-Kapselung kann bei komplexeren Anwendungen zu größeren Problemen führen. Hier kommt Shadow DOM ins Spiel.
Shadow Root anfügen
Hängen Sie ein Shadow Root an das Element an und rendern Sie das DOM in diesem Root:
index.js
class RatingElement extends HTMLElement {
constructor() {
super();
this.rating = 0;
}
connectedCallback() {
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<style>
:host {
display: inline-flex;
align-items: center;
}
button {
background: transparent;
border: none;
cursor: pointer;
}
</style>
<button class="thumb_down" >
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating">${this.rating}</span>
<button class="thumb_up">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>
`;
}
}
customElements.define('rating-element', RatingElement);
Wenn Sie die Seite aktualisieren, sehen Sie, dass die Stile im Hauptdokument die Knoten im Shadow Root nicht mehr auswählen können.
Wie gehen Sie dazu vor? Im connectedCallback haben Sie this.attachShadow aufgerufen, wodurch einem Element ein Shadow Root angehängt wird. Im open-Modus können die Shadow-Inhalte untersucht werden und der Shadow-Root ist auch über this.shadowRoot zugänglich. Sehen Sie sich die Webkomponente auch im Chrome-Inspector an:

Sie sollten jetzt einen maximierbaren Schattenstamm sehen, der den Inhalt enthält. Alles innerhalb dieses Shadow-Roots wird als Shadow DOM bezeichnet. Wenn Sie das Bewertungselement in den Chrome-Entwicklertools auswählen und $0.children aufrufen, sehen Sie, dass keine untergeordneten Elemente zurückgegeben werden. Das liegt daran, dass Shadow DOM nicht als Teil desselben DOM-Baums wie direkte untergeordnete Elemente, sondern als Shadow Tree betrachtet wird.
Light DOM
Test: Fügen Sie einen Knoten als direkt untergeordnetes Element von <rating-element> hinzu:
index.html
<rating-element>
<div>
This is the light DOM!
</div>
</rating-element>
Wenn Sie die Seite aktualisieren, sehen Sie, dass dieser neue DOM-Knoten im Light DOM dieses benutzerdefinierten Elements nicht auf der Seite angezeigt wird. Das liegt daran, dass Shadow DOM Funktionen bietet, mit denen gesteuert werden kann, wie Light-DOM-Knoten über <slot>-Elemente in das Shadow DOM projiziert werden.
5. HTML-Vorlagen
Vorteile von Vorlagen
Die Verwendung von innerHTML und Template-Literal-Strings ohne Bereinigung kann zu Sicherheitsproblemen durch Script-Injection führen. In der Vergangenheit wurden unter anderem DocumentFragments verwendet. Diese haben jedoch auch andere Probleme, z. B. das Laden von Bildern und das Ausführen von Skripts, wenn die Vorlagen definiert werden, und sie erschweren die Wiederverwendung. Hier kommt das <template>-Element ins Spiel. Vorlagen bieten inerten DOM, eine leistungsstarke Methode zum Klonen von Knoten und wiederverwendbare Vorlagen.
Vorlagen verwenden
Stellen Sie als Nächstes die Komponente auf die Verwendung von HTML-Vorlagen um:
index.html
<body>
<template id="rating-element-template">
<style>
:host {
display: inline-flex;
align-items: center;
}
button {
background: transparent;
border: none;
cursor: pointer;
}
</style>
<button class="thumb_down" >
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating"></span>
<button class="thumb_up">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>
</template>
<rating-element>
<div>
This is the light DOM!
</div>
</rating-element>
</body>
Hier haben Sie den DOM-Inhalt in ein <code><template></code>-Tag im DOM des Hauptdokuments verschoben. Definieren Sie das benutzerdefinierte Element jetzt neu:
index.js
class RatingElement extends HTMLElement {
constructor() {
super();
this.rating = 0;
}
connectedCallback() {
const shadowRoot = this.attachShadow({mode: 'open'});
const templateContent = document.getElementById('rating-element-template').content;
const clonedContent = templateContent.cloneNode(true);
shadowRoot.appendChild(clonedContent);
this.shadowRoot.querySelector('.rating').innerText = this.rating;
}
}
customElements.define('rating-element', RatingElement);
Um dieses Vorlagenelement zu verwenden, fragen Sie die Vorlage ab, rufen Sie ihren Inhalt ab und klonen Sie die Knoten mit templateContent.cloneNode. Das Argument true führt dabei einen Deep Clone durch. Anschließend initialisieren Sie das DOM mit den Daten.
Herzlichen Glückwunsch! Sie haben jetzt eine Webkomponente. Leider passiert noch nichts. Fügen Sie also als Nächstes einige Funktionen hinzu.
6. Funktionen hinzufügen
Property-Bindungen
Derzeit ist die einzige Möglichkeit, die Bewertung für das Bewertungselement festzulegen, das Element zu erstellen, die rating-Eigenschaft für das Objekt festzulegen und es dann auf der Seite zu platzieren. Leider funktionieren native HTML-Elemente in der Regel nicht so. Native HTML-Elemente werden in der Regel sowohl bei Änderungen von Attributen als auch von Properties aktualisiert.
Sorgen Sie dafür, dass das benutzerdefinierte Element die Ansicht aktualisiert, wenn sich die rating-Eigenschaft ändert, indem Sie die folgenden Zeilen hinzufügen:
index.js
constructor() {
super();
this._rating = 0;
}
set rating(value) {
this._rating = value;
if (!this.shadowRoot) {
return;
}
const ratingEl = this.shadowRoot.querySelector('.rating');
if (ratingEl) {
ratingEl.innerText = this._rating;
}
}
get rating() {
return this._rating;
}
Sie fügen einen Setter und einen Getter für die Eigenschaft „rating“ hinzu und aktualisieren dann den Text des Elements „rating“, sofern es verfügbar ist. Wenn Sie die Eigenschaft „rating“ für das Element festlegen, wird die Ansicht aktualisiert. Testen Sie das in der DevTools-Konsole.
Attributbindung
Aktualisieren Sie nun die Ansicht, wenn sich das Attribut ändert. Das ist ähnlich wie bei einer Eingabe, deren Ansicht aktualisiert wird, wenn Sie <input value="newValue"> festlegen. Glücklicherweise enthält der Lebenszyklus von Webkomponenten die attributeChangedCallback. Aktualisieren Sie die Altersfreigabe, indem Sie die folgenden Zeilen hinzufügen:
index.js
static get observedAttributes() {
return ['rating'];
}
attributeChangedCallback(attributeName, oldValue, newValue) {
if (attributeName === 'rating') {
const newRating = Number(newValue);
this.rating = newRating;
}
}
Damit attributeChangedCallback ausgelöst wird, müssen Sie einen statischen Getter für RatingElement.observedAttributes which defines the attributes to be observed for changes festlegen. Anschließend legen Sie die Altersfreigabe deklarativ im DOM fest. Probieren Sie es aus:
index.html
<rating-element rating="5"></rating-element>
Die Bewertung sollte jetzt deklarativ aktualisiert werden.
Schaltflächenfunktionen
Jetzt fehlt nur noch die Schaltflächenfunktion. Das Verhalten dieser Komponente sollte es dem Nutzer ermöglichen, eine einzelne Upvote- oder Downvote-Bewertung abzugeben, und dem Nutzer visuelles Feedback geben. Sie können dies mit einigen Event-Listenern und einer reflektierenden Eigenschaft implementieren. Aktualisieren Sie aber zuerst die Stile, um visuelles Feedback zu geben, indem Sie die folgenden Zeilen anhängen:
index.html
<style>
...
:host([vote=up]) .thumb_up {
fill: green;
}
:host([vote=down]) .thumb_down {
fill: red;
}
</style>
Im Shadow DOM verweist der Selektor :host auf den Knoten oder das benutzerdefinierte Element, an das der Shadow Root angehängt ist. Wenn das Attribut vote in diesem Fall "up" ist, wird der Daumen nach oben grün angezeigt. Wenn vote "down", then it will turn the thumb-down button red ist, Implementieren Sie nun die Logik dafür, indem Sie eine entsprechende Property / ein entsprechendes Attribut für vote erstellen, ähnlich wie Sie rating implementiert haben. Beginnen Sie mit dem Setter und Getter für die Property:
index.js
constructor() {
super();
this._rating = 0;
this._vote = null;
}
set vote(newValue) {
const oldValue = this._vote;
if (newValue === oldValue) {
return;
}
if (newValue === 'up') {
if (oldValue === 'down') {
this.rating += 2;
} else {
this.rating += 1;
}
} else if (newValue === 'down') {
if (oldValue === 'up') {
this.rating -= 2;
} else {
this.rating -= 1;
}
}
this._vote = newValue;
this.setAttribute('vote', newValue);
}
get vote() {
return this._vote;
}
Sie initialisieren die Instanzeigenschaft _vote mit null im constructor und prüfen im Setter, ob sich der neue Wert unterscheidet. Wenn ja, passen Sie die Bewertung entsprechend an und geben Sie das Attribut vote mit this.setAttribute an den Host zurück.
Richten Sie als Nächstes die Attributbindung ein:
index.js
static get observedAttributes() {
return ['rating', 'vote'];
}
attributeChangedCallback(attributeName, oldValue, newValue) {
if (attributeName === 'rating') {
const newRating = Number(newValue);
this.rating = newRating;
} else if (attributeName === 'vote') {
this.vote = newValue;
}
}
Das ist derselbe Vorgang, den Sie beim Binden des Attributs rating durchlaufen haben: Sie fügen vote dem observedAttributes hinzu und legen die Property vote im attributeChangedCallback fest. Fügen Sie nun Click-Event-Listener hinzu, um den Schaltflächen Funktionalität zu verleihen.
index.js
constructor() {
super();
this._rating = 0;
this._vote = null;
this._boundOnUpClick = this._onUpClick.bind(this);
this._boundOnDownClick = this._onDownClick.bind(this);
}
connectedCallback() {
...
this.shadowRoot.querySelector('.thumb_up')
.addEventListener('click', this._boundOnUpClick);
this.shadowRoot.querySelector('.thumb_down')
.addEventListener('click', this._boundOnDownClick);
}
disconnectedCallback() {
this.shadowRoot.querySelector('.thumb_up')
.removeEventListener('click', this._boundOnUpClick);
this.shadowRoot.querySelector('.thumb_down')
.removeEventListener('click', this._boundOnDownClick);
}
_onUpClick() {
this.vote = 'up';
}
_onDownClick() {
this.vote = 'down';
}
Im constructor binden Sie einige Klick-Listener an das Element und behalten die Referenzen bei. Im connectedCallback werden Click-Events für die Schaltflächen erfasst. Im disconnectedCallback bereinigen Sie diese Listener und legen vote für die Klick-Listener selbst entsprechend fest.
Herzlichen Glückwunsch! Sie haben jetzt eine voll funktionsfähige Webkomponente. Klicken Sie auf einige Schaltflächen, um sie auszuprobieren. Das Problem ist nun, dass meine JS-Datei 96 Zeilen und meine HTML-Datei 43 Zeilen umfasst. Der Code ist für eine so einfache Komponente recht ausführlich und imperativ. Hier kommt das Lit-Projekt von Google ins Spiel.
7. Lit-html
Code-Checkpoint
Warum lit-html?
Das <template>-Tag ist zwar nützlich und leistungsstark, aber es ist nicht mit der Logik der Komponente verknüpft. Daher ist es schwierig, die Vorlage mit der restlichen Logik zu verteilen. Außerdem führt die Art und Weise, wie Vorlagenelemente verwendet werden, naturgemäß zu imperativem Code, der in vielen Fällen weniger gut lesbar ist als deklarative Codierungsmuster.
Hier kommt lit-html ins Spiel. Lit-HTML ist das Rendering-System von Lit, mit dem Sie HTML-Vorlagen in JavaScript schreiben und diese Vorlagen dann effizient zusammen mit Daten rendern und neu rendern können, um das DOM zu erstellen und zu aktualisieren. Sie ähnelt den beliebten JSX- und VDOM-Bibliotheken, wird aber nativ im Browser ausgeführt und ist in vielen Fällen viel effizienter.
Lit HTML verwenden
Als Nächstes migrieren Sie die native Webkomponente rating-element zur Verwendung von Lit-Vorlagen, die Tagged Template Literals verwenden. Das sind Funktionen, die Vorlagenstrings mit einer speziellen Syntax als Argumente verwenden. Lit verwendet dann im Hintergrund Vorlagenelemente, um ein schnelles Rendern zu ermöglichen und einige Bereinigungsfunktionen für die Sicherheit bereitzustellen. Migrieren Sie zuerst die <template> in index.html in eine Lit-Vorlage, indem Sie der Webkomponente eine render()-Methode hinzufügen:
index.js
// Dont forget to import from Lit!
import {render, html} from 'lit';
class RatingElement extends HTMLElement {
...
render() {
if (!this.shadowRoot) {
return;
}
const template = html`
<style>
:host {
display: inline-flex;
align-items: center;
}
button {
background: transparent;
border: none;
cursor: pointer;
}
:host([vote=up]) .thumb_up {
fill: green;
}
:host([vote=down]) .thumb_down {
fill: red;
}
</style>
<button class="thumb_down">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating">${this.rating}</span>
<button class="thumb_up">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>`;
render(template, this.shadowRoot);
}
}
Sie können die Vorlage auch unter index.html löschen. In dieser Rendermethode definieren Sie eine Variable namens template und rufen die Funktion html auf, die als Tagged Template Literal definiert ist. Außerdem haben Sie im span.rating-Element eine einfache Datenbindung mit der Vorlagenliteral-Interpolationssyntax ${...} durchgeführt. Das bedeutet, dass Sie diesen Knoten irgendwann nicht mehr imperativ aktualisieren müssen. Außerdem rufen Sie die Lit-Methode render auf, die die Vorlage synchron in den Shadow Root rendert.
Migration zur deklarativen Syntax
Nachdem Sie das <template>-Element entfernt haben, können Sie den Code umgestalten, um stattdessen die neu definierte render-Methode aufzurufen. Sie können damit beginnen, die Event-Listener-Bindung von Lit zu nutzen, um den Listener-Code zu bereinigen:
index.js
<button
class="thumb_down"
@click=${() => {this.vote = 'down'}}>
...
<button
class="thumb_up"
@click=${() => {this.vote = 'up'}}>
In Lit-Vorlagen kann mit der Bindungssyntax @EVENT_NAME ein Event-Listener für einen Knoten hinzugefügt werden. In diesem Fall wird die Eigenschaft vote jedes Mal aktualisiert, wenn auf diese Schaltflächen geklickt wird.
Bereinigen Sie als Nächstes den Initialisierungscode für den Ereignis-Listener in constructor, connectedCallback und disconnectedCallback:
index.js
constructor() {
super();
this._rating = 0;
this._vote = null;
}
connectedCallback() {
this.attachShadow({mode: 'open'});
this.render();
}
// remove disonnectedCallback and _onUpClick and _onDownClick
Sie konnten die Click-Listener-Logik aus allen drei Callbacks und sogar die disconnectedCallback vollständig entfernen. Außerdem konnten Sie den gesamten DOM-Initialisierungscode aus dem connectedCallback entfernen, wodurch er viel eleganter aussieht. Das bedeutet auch, dass Sie die Listener-Methoden _onUpClick und _onDownClick nicht mehr benötigen.
Aktualisieren Sie schließlich die Property-Setter, damit die Methode render verwendet wird und das DOM aktualisiert werden kann, wenn sich die Properties oder Attribute ändern:
index.js
set rating(value) {
this._rating = value;
this.render();
}
...
set vote(newValue) {
const oldValue = this._vote;
if (newValue === oldValue) {
return;
}
if (newValue === 'up') {
if (oldValue === 'down') {
this.rating += 2;
} else {
this.rating += 1;
}
} else if (newValue === 'down') {
if (oldValue === 'up') {
this.rating -= 2;
} else {
this.rating -= 1;
}
}
this._vote = newValue;
this.setAttribute('vote', newValue);
// add render method
this.render();
}
Hier konnten Sie die Logik für DOM-Aktualisierungen aus dem Setter für rating entfernen und einen Aufruf von render aus dem Setter für vote hinzufügen. Die Vorlage ist jetzt viel besser lesbar, da Sie sehen können, wo die Bindungen und Event-Listener angewendet werden.
Aktualisieren Sie die Seite. Sie sollten jetzt eine funktionierende Schaltfläche zum Bewerten sehen, die so aussieht, wenn Sie auf „Mag ich“ klicken.

8. LitElement
Warum LitElement?
Es gibt immer noch einige Probleme mit dem Code. Wenn Sie zuerst das Attribut oder die Eigenschaft vote ändern, kann sich dadurch die Eigenschaft rating ändern, was dazu führt, dass render zweimal aufgerufen wird. Obwohl wiederholte Aufrufe von „render“ im Wesentlichen keine Operationen sind und effizient sind, verbringt die JavaScript-VM immer noch Zeit damit, diese Funktion zweimal synchron aufzurufen. Zweitens ist das Hinzufügen neuer Properties und Attribute mühsam, da viel Boilerplate-Code erforderlich ist. Hier kommt LitElement ins Spiel.
LitElement ist die Basisklasse von Lit zum Erstellen schneller, einfacher Webkomponenten, die in verschiedenen Frameworks und Umgebungen verwendet werden können. Als Nächstes sehen wir uns an, was LitElement für uns in rating-element tun kann, indem wir die Implementierung entsprechend ändern.
LitElement verwenden
Importieren Sie zuerst die Basisklasse LitElement aus dem Paket lit und erstellen Sie eine Unterklasse:
index.js
import {LitElement, html, css} from 'lit';
class RatingElement extends LitElement {
// remove connectedCallback()
...
Sie importieren LitElement, die neue Basisklasse für rating-element. Als Nächstes behalten Sie den html-Import bei und schließlich css, wodurch wir CSS-Tagged-Template-Literale für CSS-Mathematik, Templating und andere Funktionen im Hintergrund definieren können.
Verschieben Sie als Nächstes die Stile aus der Rendermethode in das statische Stylesheet von Lit:
index.js
class RatingElement extends LitElement {
static get styles() {
return css`
:host {
display: inline-flex;
align-items: center;
}
button {
background: transparent;
border: none;
cursor: pointer;
}
:host([vote=up]) .thumb_up {
fill: green;
}
:host([vote=down]) .thumb_down {
fill: red;
}
`;
}
...
Hier sind die meisten Stile in Lit enthalten. Lit verwendet diese Stile und Browserfunktionen wie Constructable Stylesheets, um die Rendering-Zeiten zu verkürzen. Bei Bedarf wird der Code auch durch das Web Components-Polyfill für ältere Browser geleitet.
Lebenszyklus
Lit führt zusätzlich zu den nativen Web Component-Callbacks eine Reihe von Callback-Methoden für den Rendering-Lebenszyklus ein. Diese Callbacks werden ausgelöst, wenn deklarierte Lit-Eigenschaften geändert werden.
Damit Sie diese Funktion verwenden können, müssen Sie statisch deklarieren, welche Eigenschaften den Rendering-Lebenszyklus auslösen.
index.js
static get properties() {
return {
rating: {
type: Number,
},
vote: {
type: String,
reflect: true,
}
};
}
// remove observedAttributes() and attributeChangedCallback()
// remove set rating() get rating()
Hier definieren Sie, dass rating und vote den Rendering-Lebenszyklus von LitElement auslösen. Außerdem definieren Sie die Typen, die zum Konvertieren der Stringattribute in Properties verwendet werden.
<user-profile .name=${this.user.name} .age=${this.user.age}>
${this.user.family.map(member => html`
<family-member
.name=${member.name}
.relation=${member.relation}>
</family-member>`)}
</user-profile>
Außerdem wird durch das reflect-Flag für die vote-Property automatisch das vote-Attribut des Hostelements aktualisiert, das Sie manuell im vote-Setter ausgelöst haben.
Nachdem Sie den Block mit statischen Properties haben, können Sie die gesamte Logik zum Aktualisieren der Attribut- und Property-Darstellung entfernen. Das bedeutet, dass Sie die folgenden Methoden entfernen können:
connectedCallbackobservedAttributesattributeChangedCallbackrating(Setter und Getter)vote(Setter und Getter, aber die Änderungslogik aus dem Setter beibehalten)
Sie behalten die constructor bei und fügen eine neue willUpdate-Lebenszyklusmethode hinzu:
index.js
constructor() {
super();
this.rating = 0;
this.vote = null;
}
willUpdate(changedProps) {
if (changedProps.has('vote')) {
const newValue = this.vote;
const oldValue = changedProps.get('vote');
if (newValue === 'up') {
if (oldValue === 'down') {
this.rating += 2;
} else {
this.rating += 1;
}
} else if (newValue === 'down') {
if (oldValue === 'up') {
this.rating -= 2;
} else {
this.rating -= 1;
}
}
}
}
// remove set vote() and get vote()
Hier initialisieren Sie einfach rating und vote und verschieben die Setter-Logik für vote in die Lifecycle-Methode willUpdate. Die Methode willUpdate wird vor render aufgerufen, wenn eine aktualisierende Eigenschaft geändert wird, da LitElement Eigenschaftsänderungen zusammenfasst und das Rendern asynchron erfolgt. Änderungen an reaktiven Eigenschaften (z. B. this.rating) in willUpdate lösen keine unnötigen render-Lebenszyklusaufrufe aus.
render ist eine LitElement-Lebenszyklusmethode, die erfordert, dass wir eine Lit-Vorlage zurückgeben:
index.js
render() {
return html`
<button
class="thumb_down"
@click=${() => {this.vote = 'down'}}>
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating">${this.rating}</span>
<button
class="thumb_up"
@click=${() => {this.vote = 'up'}}>
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>`;
}
Sie müssen nicht mehr nach dem Shadow-Root suchen und auch die Funktion render, die zuvor aus dem Paket 'lit' importiert wurde, nicht mehr aufrufen.
Das Element sollte jetzt in der Vorschau gerendert werden. Klicken Sie darauf.
9. Glückwunsch
Sie haben erfolgreich eine Webkomponente von Grund auf erstellt und in ein LitElement umgewandelt.
Lit ist sehr klein (< 5 KB minimiert und gezippt), sehr schnell und macht wirklich Spaß beim Programmieren. Sie können Komponenten erstellen, die von anderen Frameworks verwendet werden können, oder vollständige Apps damit entwickeln.
Sie wissen jetzt, was eine Webkomponente ist, wie Sie eine erstellen und wie Lit die Entwicklung vereinfacht.
Code-Checkpoint
Möchten Sie Ihren endgültigen Code mit unserem vergleichen? Hier können Sie die beiden vergleichen.
Nächste Schritte
Sehen Sie sich einige der anderen Codelabs an.
- Lit für React-Entwickler
- Brick Viewer mit lit-element erstellen
- Story-Komponente mit lit-element erstellen
Weitere Informationen
- Interaktives Lit-Tutorial
- The Lit Docs
- Open Web Components: Eine Community, die Anleitungen und Tools bereitstellt
- WebComponents.dev: Webkomponenten in allen bekannten Frameworks erstellen
Community
- Lit and Friends Slack – Die größte Web Components-Community
- @buildWithLit auf Twitter: Das Twitter-Konto des Teams, das Lit entwickelt hat
- Web Components SF – Ein Web Components-Meetup für San Francisco