1. Einführung
Zuletzt aktualisiert:10.08.2021
Webkomponenten
Webkomponenten sind eine Reihe von Webplattform-APIs, mit denen Sie neue benutzerdefinierte, wiederverwendbare und gekapselte HTML-Tags zur Verwendung auf Webseiten und Webanwendungen erstellen können. Benutzerdefinierte Komponenten und Widgets, die auf den Web Component-Standards basieren, funktionieren mit allen modernen Browsern und können mit jeder JavaScript-Bibliothek oder jedem Framework verwendet werden, das HTML unterstützt.
Was ist Lit
Lit ist eine einfache Bibliothek zum Erstellen schneller, schlanker Webkomponenten, die in jedem Framework oder ohne Framework funktionieren. Mit Lit kannst du gemeinsam nutzbare Komponenten, Anwendungen, Designsysteme und vieles mehr erstellen.
Lit stellt APIs zur Vereinfachung gängiger Webkomponenten-Aufgaben wie der Verwaltung von Eigenschaften, Attributen und Rendering bereit.
Aufgaben in diesem Lab
- Was ist eine Webkomponente?
- Die Konzepte der Webkomponenten
- So erstellen Sie eine Webkomponente
- Was sind Lit-HTML und LitElement?
- Was Lit über eine Web-Komponente macht
Inhalt
- Eine Vanille-Webkomponente für „Mag ich“/„Mag ich nicht“
- Lit-based Web Component (Mag ich / Mag ich nicht)
Voraussetzungen
- Jeder aktualisierte moderne Browser (Chrome, Safari, Firefox, Chromium Edge) Webkomponenten funktionieren in allen modernen Browsern. Polyfills sind für Microsoft Internet Explorer 11 und Microsoft Edge ohne Chromium verfügbar.
- Kenntnisse in HTML, CSS, JavaScript und Chrome-Entwicklertools.
2. Einrichtung und Playground erkunden
Auf den Code zugreifen
Im gesamten Codelab finden Sie Links zum Lit Playground:
Der Playground ist eine Code-Sandbox, die vollständig in deinem Browser ausgeführt wird. Sie kann TypeScript- und JavaScript-Dateien kompilieren und ausführen und außerdem Importe in Knotenmodule automatisch auflösen. z.B.
// before
import './my-file.js';
import 'lit';
// after
import './my-file.js';
import 'https://unpkg.com/lit?module';
Du kannst das gesamte Tutorial im Lit Playground durchgehen und diese Checkpoints als Ausgangspunkt verwenden. Wenn Sie VS Code verwenden, können Sie diese Checkpoints nutzen, um den Startcode für einen beliebigen Schritt herunterzuladen und damit Ihre Arbeit zu überprüfen.
Erkundung der beleuchteten Spielplatz-Benutzeroberfläche
Im Screenshot der Benutzeroberfläche von Lit Playground sind die Abschnitte hervorgehoben, die Sie in diesem Codelab verwenden werden.
- Dateiauswahl. Beachten Sie das Pluszeichen...
- Dateieditor.
- Codevorschau.
- Schaltfläche „Aktualisieren“.
- Grafik: Symbol zum Herunterladen
VS Code-Einrichtung (erweitert)
Dies sind die Vorteile dieser VS Code-Einrichtung:
- Prüfung des Vorlagentyps
- Template Intellisense & automatische Vervollständigung
Wenn Sie NPM und VS Code (mit dem lit-plugin-Plug-in) bereits installiert haben und wissen, wie Sie diese Umgebung verwenden können, können Sie diese Projekte einfach herunterladen und starten. Gehen Sie dazu so vor:
- Klicken Sie auf die Schaltfläche zum Herunterladen.
- Inhalt der TAR-Datei in ein Verzeichnis extrahieren
- Installieren Sie einen dev-Server, der Bare-Modul-Bezeichner auflösen kann. Das Lit-Team empfiehlt @web/dev-server.
- Hier ein Beispiel:
package.json
- Hier ein Beispiel:
- Führen Sie den dev-Server aus und öffnen Sie Ihren Browser (wenn Sie
@web/dev-server
verwenden, können Sienpx web-dev-server --node-resolve --watch --open
verwenden)- Wenn Sie das Beispiel
package.json
verwenden, verwenden Sienpm run serve
.
- Wenn Sie das Beispiel
3. Benutzerdefiniertes Element definieren
Benutzerdefinierte Elemente
Webkomponenten sind eine Sammlung von vier nativen Web-APIs. Sie sind:
- ES-Module
- Benutzerdefinierte Elemente
- Schatten-DOM
- HTML-Vorlagen
Du hast bereits die ES-Modulspezifikation verwendet, mit der du JavaScript-Module mit Importen und Exporten erstellen kannst, die mit <script type="module">
in die Seite geladen werden.
Ein benutzerdefiniertes Element definieren
Mit der Spezifikation für benutzerdefinierte Elemente können Nutzer ihre eigenen HTML-Elemente mithilfe von JavaScript definieren. Die Namen müssen einen Bindestrich (-
) enthalten, damit sie von nativen Browserelementen unterschieden werden können. 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üge ein <rating-element>
-Objekt in den Dokumenttext ein und sieh dir an, was gerendert wird.
index.html
<body>
<rating-element></rating-element>
</body>
In der Ausgabe sehen Sie, dass nichts gerendert wurde. Das ist ganz normal, weil du dem Browser nicht mitgeteilt hast, wie <rating-element>
gerendert werden soll. Sie können prüfen, ob die Definition des benutzerdefinierten Elements erfolgreich war, indem Sie in den Chrome-Entwicklertools das <rating-element>
auswählen. Element-Selektor und in der Konsole den Aufruf:
$0.constructor
Es sollte Folgendes ausgegeben werden:
class RatingElement extends HTMLElement {}
Lebenszyklus benutzerdefinierter Elemente
Benutzerdefinierte Elemente enthalten eine Reihe von Lebenszyklus-Hooks. Sie sind:
constructor
connectedCallback
disconnectedCallback
attributeChangedCallback
adoptedCallback
constructor
wird beim erstmaligen Erstellen des Elements aufgerufen, z. B. durch Aufrufen von document.createElement(‘rating-element')
oder new RatingElement()
. Der Konstruktor eignet sich gut zum Einrichten Ihres Elements, es wird jedoch in der Regel als nicht praktikabel angesehen, DOM-Änderungen im Konstruktor für das Element "Boot-up" durchzuführen. aus Gründen der Leistung.
connectedCallback
wird aufgerufen, wenn das benutzerdefinierte Element an das DOM angehängt wird. Hier finden in der Regel die ersten DOM-Änderungen statt.
disconnectedCallback
wird aufgerufen, nachdem das benutzerdefinierte Element aus dem DOM entfernt wurde.
attributeChangedCallback(attrName, oldValue, newValue)
wird aufgerufen, wenn sich eines der benutzerdefinierten Attribute ändert.
Das adoptedCallback
wird aufgerufen, wenn das benutzerdefinierte Element von einem anderen documentFragment
über adoptNode
in das Hauptdokument übernommen wird, z. B. in HTMLTemplateElement
.
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);
In der constructor
speichern Sie eine Instanzeigenschaft namens rating
für das Element. In der connectedCallback
fügen Sie <rating-element>
untergeordnete DOM-Elemente hinzu, um die aktuelle Bewertung sowie die Schaltflächen „Mag ich“ und „Mag ich nicht“ anzuzeigen.
4. Schatten-DOM
Warum Shadow DOM?
Im vorherigen Schritt werden Sie feststellen, dass die Selektoren im Stil-Tag, das Sie eingefügt haben, ein beliebiges Bewertungselement auf der Seite sowie alle Schaltflächen auswählen. Dies kann dazu führen, dass die Stile aus dem Element herausragen und andere Knoten auswählen, die Sie möglicherweise nicht gestalten möchten. Darüber hinaus können die Knoten innerhalb Ihres benutzerdefinierten Elements durch andere Stile außerhalb dieses benutzerdefinierten Elements ungewollt formatiert werden. Versuchen Sie beispielsweise, ein Stil-Tag in den Header des Hauptdokuments einzufügen:
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 roten Rahmen um die Spanne für die Bewertung haben. Dies ist ein trivialer Fall, aber die fehlende DOM-Kapselung kann bei komplexeren Anwendungen zu größeren Problemen führen. Hier kommt Shadow DOM ins Spiel.
Schattenstamm hinzufügen
Hängen Sie einen Schattenstamm an das Element an und rendern Sie das DOM innerhalb dieses Stamms:
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, werden Sie feststellen, dass die Knoten im Shadow Root nicht mehr mit den Stilen im Hauptdokument ausgewählt werden können.
Wie gehen Sie dazu vor? In der connectedCallback
haben Sie this.attachShadow
aufgerufen, wodurch ein Schattenstamm an ein Element angehängt wird. Der Modus open
bedeutet, dass der Schatteninhalt inspiziert werden kann und auch der Schattenstamm über this.shadowRoot
zugänglich ist. Sehen Sie sich auch die Webkomponente im Chrome Inspector an:
Sie sollten nun einen maximierbaren Schattenstamm sehen, der den Inhalt enthält. Alles in dieser Schattenwurzel wird als Shadow DOM bezeichnet. Wenn Sie das Bewertungselement in den Chrome-Entwicklertools auswählen und $0.children
aufrufen, werden Sie feststellen, dass es keine untergeordneten Elemente zurückgibt. Das liegt daran, dass das Shadow DOM nicht als Teil desselben DOM-Baums wie direkte untergeordnete Elemente angesehen wird, sondern als Schattenbaum.
Light DOM
Experiment: Fügen Sie einen Knoten als direktes untergeordnetes Element von <rating-element>
hinzu:
index.html
<rating-element>
<div>
This is the light DOM!
</div>
</rating-element>
Wenn Sie die Seite aktualisieren, werden Sie feststellen, dass dieser neue DOM-Knoten im Light DOM dieses benutzerdefinierten Elements nicht auf der Seite angezeigt wird. Das liegt daran, dass Shadow DOM Funktionen hat, mit denen gesteuert wird, wie Light DOM-Knoten über <slot>
-Elemente in den Schattenbereich projiziert werden.
5. HTML-Vorlagen
Warum Vorlagen?
Die Verwendung von innerHTML
und Vorlagenliteral-Strings ohne Bereinigung kann zu Sicherheitsproblemen bei der Skripteinschleusung führen. In der Vergangenheit war die Verwendung von DocumentFragment
s z. B. mit anderen Problemen verbunden, z. B. beim Laden von Bildern und beim Ausführen von Skripts, wenn die Vorlagen definiert wurden. Auch die Wiederverwendbarkeit wird behindert. Hier kommt das Element <template>
ins Spiel: Vorlagen bieten ein inaktives DOM, eine leistungsstarke Methode zum Klonen von Knoten, sowie 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 Vorlagen-Tag im DOM des Hauptdokuments verschoben. Refaktorieren Sie nun die Definition des benutzerdefinierten Elements:
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);
Wenn Sie dieses Vorlagenelement verwenden möchten, fragen Sie die Vorlage ab, rufen ihren Inhalt ab und klonen diese Knoten mit templateContent.cloneNode
, wobei das Argument true
einen Deep-Klon ausführt. Anschließend initialisieren Sie den Dom mit den Daten.
Herzlichen Glückwunsch, Sie haben jetzt eine Webkomponente! Leider ist noch nichts, also fügen Sie als Nächstes einige Funktionen hinzu.
6. Funktion hinzufügen
Property-Bindungen
Derzeit kann die Bewertung für das Bewertungselement nur festgelegt werden, indem das Element konstruiert, die Eigenschaft rating
für das Objekt festgelegt und es dann auf der Seite eingefügt wird. Leider funktionieren native HTML-Elemente normalerweise nicht so. Native HTML-Elemente werden tendenziell bei Änderungen an Eigenschaften und Attributen aktualisiert.
Damit die Ansicht durch das benutzerdefinierte Element aktualisiert wird, wenn sich die Eigenschaft rating
ändert, fügen Sie die folgenden Zeilen hinzu:
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 Getter für die Eigenschaft „rating“ hinzu und aktualisieren dann den Text des Bewertungselements, sofern verfügbar. Wenn Sie also die Eigenschaft rating für das Element festlegen, wird die Ansicht aktualisiert. können Sie es kurz in der Entwicklertools-Konsole testen.
Attributbindungen
Aktualisieren Sie nun die Ansicht, wenn sich das Attribut ändert. Dies ähnelt einer Eingabe, die ihre Ansicht aktualisiert, wenn Sie <input value="newValue">
festlegen. Glücklicherweise umfasst der Lebenszyklus der Webkomponente die attributeChangedCallback
. Aktualisieren Sie die Bewertung, 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, musst du einen statischen Getter für RatingElement.observedAttributes which defines the attributes to be observed for changes
festlegen. Anschließend legen Sie die Bewertung 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 fehlen nur noch die Schaltflächen. Das Verhalten dieser Komponente sollte es dem Nutzer ermöglichen, eine einzelne positive oder negative Bewertung abzugeben und dem Nutzer visuelles Feedback zu geben. Sie können dies mit einigen Ereignis-Listenern und einer spiegelnden -Eigenschaft implementieren, aber zuerst aktualisieren Sie 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 bezieht sich der :host
-Selektor auf den Knoten oder das benutzerdefinierte Element, mit dem der Schattenstamm verknüpft ist. Wenn das Attribut vote
den Wert "up"
hat, ist die Schaltfläche mit dem Daumen nach oben grün, aber wenn vote
auf "down", then it will turn the thumb-down button red
gesetzt ist. Implementieren Sie nun die entsprechende Logik, indem Sie eine reflektierende Eigenschaft bzw. ein reflektierendes Attribut für vote
erstellen, ähnlich wie bei der Implementierung von rating
. Beginnen Sie mit dem Property-Setter und -Getter:
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 das Instanzattribut _vote
mit null
im constructor
und prüfen im Setter, ob der neue Wert anders ist. In diesem Fall passen Sie die Bewertung entsprechend an und geben vor allem das Attribut vote
mit this.setAttribute
an den Host zurück.
Als Nächstes richten Sie 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;
}
}
Dies ist derselbe Vorgang, den Sie bei der rating
-Attributbindung ausgeführt haben: fügen Sie vote
dem observedAttributes
hinzu und legen die Eigenschaft vote
in der attributeChangedCallback
fest. Fügen Sie nun einige Click-Event-Listener hinzu, um den Schaltflächen Funktionen zu bieten.
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';
}
In der constructor
binden Sie einige Klick-Listener an das Element und behalten die Verweise bei. In der connectedCallback
werden Click-Events auf die Schaltflächen überwacht. In der disconnectedCallback
bereinigen Sie diese Listener und auf den Klick-Listenern selbst legen Sie vote
entsprechend fest.
Herzlichen Glückwunsch! Sie verfügen nun über eine Webkomponente mit allen Funktionen. klicken Sie auf ein paar Schaltflächen! Das Problem ist, dass meine JS-Datei jetzt 96 Zeilen erreicht, meine HTML-Datei 43 Zeilen und der Code ist ziemlich ausführlich und für eine so einfache Komponente unerlässlich. Hier kommt das Lit-Projekt von Google ins Spiel!
7. Lit-HTML
Code-Checkpoint
Warum Lit-HTML?
In erster Linie ist das <template>
-Tag nützlich und leistungsfähig, aber es ist nicht mit der Logik der Komponente verpackt, sodass es schwierig ist, die Vorlage mit dem Rest der Logik zu verteilen. Außerdem verleiht die Art und Weise, wie Vorlagenelemente verwendet werden, imperativen Code, was im Vergleich zu deklarativen Codierungsmustern in vielen Fällen zu weniger lesbarem Code führt.
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 gemeinsam mit Daten effizient rendern und neu rendern können, um DOM zu erstellen und zu aktualisieren. Es ähnelt den beliebten JSX- und VDOM-Bibliotheken, wird jedoch nativ im Browser ausgeführt und ist in vielen Fällen viel effizienter.
Lit HTML verwenden
Migrieren Sie als Nächstes die native Webkomponente rating-element
zur Verwendung von Lit-Vorlagen, die getaggte Vorlagenliterale verwenden. Dabei handelt es sich um Funktionen, die Vorlagenstrings als Argumente mit einer speziellen Syntax annehmen. Die Lit nutzt dann Vorlagenelemente im Hintergrund, um ein schnelles Rendering zu ermöglichen und einige Sicherheitsfunktionen zur Verfügung zu stellen. Migrieren Sie zuerst <template>
aus index.html
in eine Lit-Vorlage. Fügen Sie dazu der WebComponent eine render()
-Methode hinzu:
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 Ihre Vorlage auch aus index.html
löschen. Bei dieser Renderingmethode definieren Sie eine Variable namens template
und rufen die mit html
getaggte Vorlagenliteralfunktion auf. Außerdem werden Sie feststellen, dass Sie eine einfache Datenbindung innerhalb des span.rating
-Elements durchgeführt haben, indem Sie die Vorlagen-Literal-Interpolationssyntax ${...}
verwendet haben. Dies bedeutet, dass Sie diesen Knoten später nicht mehr zwingend aktualisieren müssen. Außerdem rufen Sie die beleuchtete render
-Methode auf, mit der die Vorlage synchron im Schattenstamm gerendert wird.
Zur deklarativen Syntax migrieren
Nachdem Sie das <template>
-Element entfernt haben, refaktorieren Sie den Code so, dass stattdessen die neu definierte Methode render
aufgerufen wird. Sie können zunächst die Event-Listener-Bindung von Licht 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'}}>
Mit Lit-Vorlagen kann einem Knoten mit der Bindungssyntax @EVENT_NAME ein Ereignis-Listener hinzugefügt werden. In diesem Fall wird das Attribut vote
jedes Mal aktualisiert, wenn auf diese Schaltflächen geklickt wird.
Bereinigen Sie als Nächstes den Initialisierungscode des Event-Listeners 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 Klick-Listener-Logik aus allen drei Callbacks entfernen und sogar disconnectedCallback
vollständig entfernen. Außerdem konnten Sie den gesamten DOM-Initialisierungscode aus der Datei connectedCallback
entfernen, um sie deutlich eleganter zu gestalten. Das bedeutet auch, dass Sie die Listener-Methoden _onUpClick
und _onDownClick
entfernen können.
Aktualisieren Sie schließlich die Attribut-Setter, um die render
-Methode zu verwenden, damit der Dom aktualisiert werden kann, wenn sich die Attribute 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 die Aktualisierung der Domain aus dem rating
-Setter entfernen und einen Aufruf von render
aus dem vote
-Setter hinzugefügt. Die Vorlage ist jetzt viel besser lesbar, da Sie sehen können, wo die Bindungen und Event-Listener angewendet werden.
Nachdem Sie die Seite aktualisiert haben, sollte eine funktionierende Bewertungsschaltfläche angezeigt werden, die so aussehen sollte, wenn Sie auf die positive Bewertung klicken.
8. LitElement
Warum LitElement
Es liegen immer noch einige Probleme mit dem Code vor. Erstens: Wenn du die Eigenschaft oder das Attribut vote
änderst, kann dies die Eigenschaft rating
ändern, was dazu führt, dass render
zweimal aufgerufen wird. Obwohl wiederholte Rendering-Aufrufe im Grunde betriebsunabhängig und effizient sind, verbringt die JavaScript-VM immer noch Zeit, diese Funktion zweimal synchron aufzurufen. Zweitens ist es mühsam, neue Eigenschaften und Attribute hinzuzufügen, da es sehr viel Boilerplate-Code erfordert. Hier kommt LitElement
ins Spiel.
LitElement
ist die Basisklasse von Lit zum Erstellen schneller, einfacher Webkomponenten, die in Frameworks und Umgebungen verwendet werden können. Sehen Sie sich als Nächstes an, was LitElement
für uns in rating-element
tun kann, indem Sie die Implementierung ändern, um sie zu verwenden.
LitElement verwenden
Importieren Sie zuerst die LitElement
-Basisklasse und erstellen Sie abgeleitete Klassen aus dem lit
-Paket:
index.js
import {LitElement, html, css} from 'lit';
class RatingElement extends LitElement {
// remove connectedCallback()
...
Sie importieren LitElement
. Dies ist die neue Basisklasse für die rating-element
. Als Nächstes behalten Sie den Import „html
“ und schließlich „css
“ bei. So können wir Vorlagenliterale mit CSS-Tags für CSS-Mathematik, Vorlagen und andere Funktionen definieren.
Verschieben Sie als Nächstes die Stile von der Rendering-Methode 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 befinden sich die meisten Stile in Lit. Die Bibliotheken werden auf diese Stile zurückgreifen und Browserfunktionen wie konstruierbare Stylesheets verwenden, um schnellere Renderingzeiten zu ermöglichen. Außerdem werden die Daten bei Bedarf über das Polyfill in älteren Browsern durch das Webkomponenten-Polyfill bereitgestellt.
Lebenszyklus
In Lit wird zusätzlich zu den nativen Webkomponenten-Callbacks eine Reihe von Callback-Methoden für den Renderinglebenszyklus eingeführt. 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 Attribute 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 LitElement-Rendering-Lebenszyklus auslösen und die Typen definieren, die zum Konvertieren der Stringattribute in Attribute 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 aktualisiert das Flag reflect
für die Property vote
automatisch das vote
-Attribut des Hostelements, das du manuell im vote
-Setter ausgelöst hast.
Da Sie nun über den Block für statische Eigenschaften verfügen, können Sie die Logik für die Aktualisierung von Attributen und Eigenschaften entfernen. Das bedeutet, dass Sie die folgenden Methoden entfernen können:
connectedCallback
observedAttributes
attributeChangedCallback
rating
(Setter und Getter)vote
(Setter und Getter, aber die Änderungslogik des Setters beibehalten)
Sie behalten jedoch die constructor
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 vote
in die Lebenszyklusmethode willUpdate
. Die Methode willUpdate
wird immer vor render
aufgerufen, wenn eine aktualisierende Eigenschaft geändert wird, da LitElement mehrere Attributänderungen in Batches fasst und das Rendering asynchron ausführt. Änderungen an reaktiven Attributen wie this.rating
in willUpdate
lösen keine unnötigen render
-Lebenszyklusaufrufe aus.
Schließlich ist render
eine LitElement-Lebenszyklusmethode, bei der eine Lit-Vorlage zurückgegeben werden muss:
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 Schattenstamm suchen und die Funktion render
, die zuvor aus dem Paket 'lit'
importiert wurde, nicht mehr aufrufen.
Ihr Element sollte jetzt in der Vorschau gerendert werden. ein Klick!
9. Glückwunsch
Herzlichen Glückwunsch, Sie haben eine Webkomponente von Grund auf erfolgreich erstellt und zu einem LitElement entwickelt!
Es ist superklein (< 5 KB reduziert und gzip), superschnell und macht das Programmieren wirklich Spaß! Sie können Komponenten für andere Frameworks erstellen oder vollwertige Anwendungen damit erstellen.
Sie wissen jetzt, was Webkomponenten sind, wie sie erstellt werden und wie sie mit Lit einfacher erstellt werden können.
Code-Checkpoint
Möchten Sie Ihren endgültigen Code mit unserem vergleichen? Hier können Sie sie vergleichen.
Was liegt als Nächstes an?
Sehen Sie sich auch die anderen Codelabs an.
- Lit für React-Entwickler
- Brick Viewer mit Lichtelement erstellen
- Komponente „Stories“ mit Lichtelement erstellen
Weitere Informationen
- Interaktive Anleitung
- The Lit Docs
- Open Web Components (Webkomponenten öffnen): Hier finden Sie Tipps und Tools, die von der Community ausgeführt werden.
- WebComponents.dev – Webkomponente in allen bekannten Frameworks erstellen
Community
- Lit and Friends Slack – die größte Web Components-Community
- @buildWithLit auf Twitter – der Twitter-Account des Teams, das Lit entwickelt hat
- Web Components SF: ein Meetup für Webkomponenten in San Francisco