Erstellen interoperabler Webkomponenten, die sogar mit React PlatoBlockchain Data Intelligence funktionieren. Vertikale Suche. Ai.

Erstellen interoperabler Webkomponenten, die sogar mit React funktionieren

Diejenigen von uns, die seit mehr als ein paar Jahren Webentwickler sind, haben wahrscheinlich Code geschrieben, der mehr als ein JavaScript-Framework verwendet. Bei all den Möglichkeiten da draußen – React, Svelte, Vue, Angular, Solid – ist es so gut wie unvermeidlich. Eines der frustrierenderen Dinge, mit denen wir uns bei der Arbeit über Frameworks auseinandersetzen müssen, ist die Neuerstellung all dieser Low-Level-UI-Komponenten: Schaltflächen, Registerkarten, Dropdowns usw. Besonders frustrierend ist, dass wir sie normalerweise in einem Framework definieren , sagen React, müssen sie dann aber umschreiben, wenn wir etwas in Svelte bauen wollen. Oder Vue. Oder solide. Usw.

Wäre es nicht besser, wenn wir diese Low-Level-UI-Komponenten einmal Framework-agnostisch definieren und sie dann zwischen Frameworks wiederverwenden könnten? Natürlich würde es! Und wir können; Webkomponenten sind der Weg. Dieser Beitrag zeigt Ihnen, wie.

Bis jetzt fehlt die SSR-Story für Webkomponenten etwas. Deklaratives Schatten-DOM (DSD) ist die Art und Weise, wie eine Webkomponente serverseitig gerendert wird, aber zum jetzigen Zeitpunkt ist sie nicht in Ihre bevorzugten Anwendungsframeworks wie Next, Remix oder SvelteKit integriert. Wenn dies für Sie erforderlich ist, überprüfen Sie unbedingt den neuesten Status von DSD. Aber ansonsten, wenn Sie SSR nicht verwenden, lesen Sie weiter.

Erstens ein Zusammenhang

Webkomponenten sind im Wesentlichen HTML-Elemente, die Sie selbst definieren, z <yummy-pizza> oder was auch immer, von Grund auf. Sie werden hier bei CSS-Tricks überall behandelt (einschließlich eine umfangreiche Serie von Caleb Williams und eine von John Rhea), aber wir gehen kurz durch den Prozess. Im Wesentlichen definieren Sie eine JavaScript-Klasse, von der Sie sie erben HTMLElement, und definieren Sie dann alle Eigenschaften, Attribute und Stile, die die Webkomponente hat, und natürlich das Markup, das sie letztendlich für Ihre Benutzer rendern wird.

Es ist aufregend, benutzerdefinierte HTML-Elemente definieren zu können, die nicht an eine bestimmte Komponente gebunden sind. Aber diese Freiheit ist auch eine Einschränkung. Unabhängig von einem JavaScript-Framework zu existieren bedeutet, dass Sie nicht wirklich mit diesen JavaScript-Frameworks interagieren können. Stellen Sie sich eine React-Komponente vor, die einige Daten abruft und dann einige rendert Sonstiges Reaktionskomponente, Weitergabe der Daten. Dies würde nicht wirklich als Webkomponente funktionieren, da eine Webkomponente nicht weiß, wie eine React-Komponente gerendert wird.

Webkomponenten zeichnen sich besonders aus als Blattbestandteile. Blattbestandteile sind das letzte, was in einem Komponentenbaum gerendert wird. Dies sind die Komponenten, die einige Requisiten erhalten und einige rendern UI. Diese sind nicht die Komponenten, die in der Mitte Ihres Komponentenbaums sitzen, Daten weitergeben, Kontext festlegen usw. – nur reine Teile davon UI das wird gleich aussehen, egal welches JavaScript-Framework den Rest der App antreibt.

Die Webkomponente, die wir erstellen

Anstatt etwas Langweiliges (und Gewöhnliches) wie einen Knopf zu bauen, lassen Sie uns etwas ein bisschen anderes bauen. In meinem Ladepfosten Wir haben uns mit der Verwendung verschwommener Bildvorschauen beschäftigt, um das Umfließen von Inhalten zu verhindern und den Benutzern eine anständige Benutzeroberfläche bereitzustellen, während unsere Bilder geladen werden. Wir haben uns die base64-Codierung verschwommener, verschlechterter Versionen unserer Bilder angesehen und dies in unserer Benutzeroberfläche angezeigt, während das echte Bild geladen wurde. Wir haben uns auch mit dem Generieren unglaublich kompakter, verschwommener Vorschauen mit einem Tool namens Blurhash.

Dieser Beitrag zeigte Ihnen, wie Sie diese Vorschauen generieren und in einem React-Projekt verwenden können. Dieser Beitrag zeigt Ihnen, wie Sie diese Vorschauen aus einer Webkomponente verwenden, damit sie von verwendet werden können jedem JavaScript-Framework.

Aber wir müssen gehen, bevor wir laufen können, also gehen wir zuerst etwas Triviales und Dummes durch, um genau zu sehen, wie Webkomponenten funktionieren.

Alles in diesem Beitrag wird Vanilla-Webkomponenten ohne Werkzeuge erstellen. Das bedeutet, dass der Code ein wenig Boilerplate haben wird, aber relativ einfach zu befolgen sein sollte. Werkzeuge wie Bett or Schablone sind zum Erstellen von Webkomponenten konzipiert und können verwendet werden, um einen Großteil dieser Boilerplate zu entfernen. Ich fordere Sie auf, sie zu überprüfen! Aber für diesen Beitrag bevorzuge ich etwas mehr Boilerplate im Austausch dafür, dass ich keine weitere Abhängigkeit einführen und lehren muss.

Eine einfache Gegenkomponente

Lassen Sie uns die klassische „Hallo Welt“ der JavaScript-Komponenten bauen: einen Zähler. Wir rendern einen Wert und eine Schaltfläche, die diesen Wert erhöht. Einfach und langweilig, aber wir werden uns die einfachstmögliche Webkomponente ansehen.

Um eine Webkomponente zu erstellen, besteht der erste Schritt darin, eine JavaScript-Klasse zu erstellen, die erbt HTMLElement:

class Counter extends HTMLElement {}

Der letzte Schritt besteht darin, die Webkomponente zu registrieren, aber nur, wenn wir sie noch nicht registriert haben:

if (!customElements.get("counter-wc")) { customElements.define("counter-wc", Counter);
}

Und natürlich wiedergeben:

<counter-wc></counter-wc>

Und alles dazwischen ist, dass wir die Webkomponente dazu bringen, das zu tun, was wir wollen. Eine gängige Lebenszyklusmethode ist connectedCallback, die ausgelöst wird, wenn unsere Webkomponente zum DOM hinzugefügt wird. Wir könnten diese Methode verwenden, um beliebige Inhalte zu rendern. Denken Sie daran, dass dies eine JS-Klasse ist, von der erbt HTMLElement, was unsere bedeutet this Wert ist das Element der Webkomponente selbst, mit all den normalen DOM-Manipulationsmethoden, die Sie bereits kennen und lieben.

Am einfachsten könnten wir dies tun:

class Counter extends HTMLElement { connectedCallback() { this.innerHTML = "<div style='color: green'>Hey</div>"; }
} if (!customElements.get("counter-wc")) { customElements.define("counter-wc", Counter);
}

… was gut funktionieren wird.

Das Wort "he" im Grün.
Erstellen interoperabler Webkomponenten, die sogar mit React funktionieren

Hinzufügen von echten Inhalten

Lassen Sie uns einige nützliche, interaktive Inhalte hinzufügen. Wir brauchen ein <span> um den aktuellen Zahlenwert zu halten und a <button> um den Zähler zu erhöhen. Im Moment erstellen wir diesen Inhalt in unserem Konstruktor und hängen ihn an, wenn sich die Webkomponente tatsächlich im DOM befindet:

constructor() { super(); const container = document.createElement('div'); this.valSpan = document.createElement('span'); const increment = document.createElement('button'); increment.innerText = 'Increment'; increment.addEventListener('click', () => { this.#value = this.#currentValue + 1; }); container.appendChild(this.valSpan); container.appendChild(document.createElement('br')); container.appendChild(increment); this.container = container;
} connectedCallback() { this.appendChild(this.container); this.update();
}

Wenn Sie von der manuellen DOM-Erstellung wirklich angeekelt sind, denken Sie daran, dass Sie festlegen können innerHTML, oder erstellen Sie sogar einmal ein Vorlagenelement als statische Eigenschaft Ihrer Webkomponentenklasse, klonen Sie es und fügen Sie die Inhalte für neue Webkomponenteninstanzen ein. Es gibt wahrscheinlich einige andere Optionen, an die ich nicht denke, oder Sie können immer ein Webkomponenten-Framework wie verwenden Bett or Schablone. Aber für diesen Beitrag werden wir es weiterhin einfach halten.

Als Nächstes benötigen wir eine einstellbare JavaScript-Klasseneigenschaft mit dem Namen value

#currentValue = 0; set #value(val) { this.#currentValue = val; this.update();
}

Es ist nur eine Standardklasseneigenschaft mit einem Setter, zusammen mit einer zweiten Eigenschaft, die den Wert enthält. Eine lustige Wendung ist, dass ich für diese Werte die Eigenschaftssyntax der privaten JavaScript-Klasse verwende. Das bedeutet, dass niemand außerhalb unserer Webkomponente diese Werte jemals berühren kann. Dies ist Standard-JavaScript das wird in allen modernen Browsern unterstützt, also scheuen Sie sich nicht, es zu benutzen.

Oder rufen Sie ihn gerne an _value wenn Sie es vorziehen. Und zu guter Letzt unsere update Verfahren:

update() { this.valSpan.innerText = this.#currentValue;
}

Es klappt!

Die Counter-Web-Komponente.
Erstellen interoperabler Webkomponenten, die sogar mit React funktionieren

Offensichtlich ist dies kein Code, den Sie in großem Umfang beibehalten möchten. Hier ist eine volle Arbeitsbeispiel wenn du genauer hinsehen möchtest. Wie ich bereits sagte, wurden Tools wie Lit und Stencil entwickelt, um dies zu vereinfachen.

Hinzufügen einiger weiterer Funktionen

Dieser Beitrag ist kein tiefer Einblick in Webkomponenten. Wir werden nicht alle APIs und Lebenszyklen abdecken; wir werden nicht einmal abdecken Schattenwurzeln oder Schlitze. Es gibt unendlich viele Inhalte zu diesen Themen. Mein Ziel hier ist es, eine ausreichend anständige Einführung zu bieten, um Interesse zu wecken, zusammen mit einigen nützlichen Anleitungen für die eigentliche Arbeit Verwendung von Webkomponenten mit den beliebten JavaScript-Frameworks, die Sie bereits kennen und lieben.

Lassen Sie uns zu diesem Zweck unsere Counter-Web-Komponente ein wenig verbessern. Nehmen wir a an color -Attribut, um die Farbe des angezeigten Werts zu steuern. Und lassen Sie es uns auch akzeptieren increment -Eigenschaft, sodass Verbraucher dieser Webkomponente sie jeweils um 2, 3, 4 erhöhen können. Und um diese Zustandsänderungen voranzutreiben, verwenden wir unseren neuen Zähler in einer Svelte-Sandbox – wir werden uns gleich mit React befassen.

Wir beginnen mit derselben Webkomponente wie zuvor und fügen ein Farbattribut hinzu. Um unsere Webkomponente so zu konfigurieren, dass sie ein Attribut akzeptiert und darauf reagiert, fügen wir eine statische hinzu observedAttributes -Eigenschaft, die die Attribute zurückgibt, auf die unsere Webkomponente wartet.

static observedAttributes = ["color"];

Damit können wir a hinzufügen attributeChangedCallback Lifecycle-Methode, die immer dann ausgeführt wird, wenn eines der in aufgelisteten Attribute auftritt observedAttributes eingestellt oder aktualisiert werden.

attributeChangedCallback(name, oldValue, newValue) { if (name === "color") { this.update(); }
}

Jetzt aktualisieren wir unsere update Methode, um es tatsächlich zu verwenden:

update() { this.valSpan.innerText = this._currentValue; this.valSpan.style.color = this.getAttribute("color") || "black";
}

Zuletzt fügen wir unsere hinzu increment Eigentum:

increment = 1;

Einfach und bescheiden.

Verwenden der Zählerkomponente in Svelte

Lassen Sie uns verwenden, was wir gerade gemacht haben. Wir gehen in unsere Svelte-App-Komponente und fügen so etwas hinzu:

<script> let color = "red";
</script> <style> main { text-align: center; }
</style> <main> <select bind:value={color}> <option value="red">Red</option> <option value="green">Green</option> <option value="blue">Blue</option> </select> <counter-wc color={color}></counter-wc>
</main>

Und es funktioniert! Unser Zähler rendert, erhöht und das Dropdown-Menü aktualisiert die Farbe. Wie Sie sehen können, rendern wir das Farbattribut in unserer Svelte-Vorlage, und wenn sich der Wert ändert, übernimmt Svelte die Beinarbeit des Aufrufs setAttribute auf unserer zugrunde liegenden Webkomponenteninstanz. Hier gibt es nichts Besonderes: Dies ist dasselbe, was es bereits für die Attribute von tut jedem HTML-Element.

Etwas interessant wird es mit dem increment Stütze. Das ist nicht ein Attribut in unserer Webkomponente; Es ist eine Stütze der Klasse der Webkomponente. Das bedeutet, dass es auf der Instanz der Webkomponente festgelegt werden muss. Haben Sie Geduld mit mir, da die Dinge in Kürze viel einfacher werden.

Zuerst fügen wir unserer Svelte-Komponente einige Variablen hinzu:

let increment = 1;
let wcInstance;

Unser Kraftpaket einer Zählerkomponente lässt Sie um 1 oder um 2 erhöhen:

<button on:click={() => increment = 1}>Increment 1</button>
<button on:click={() => increment = 2}>Increment 2</button>

Aber in der Theorie, müssen wir die tatsächliche Instanz unserer Webkomponente abrufen. Dies ist das gleiche, was wir immer tun, wenn wir a hinzufügen ref mit Reagieren. Mit Svelte ist es ganz einfach bind:this Richtlinie:

<counter-wc bind:this={wcInstance} color={color}></counter-wc>

Jetzt lauschen wir in unserem Svelte-Template auf Änderungen an der Increment-Variablen unserer Komponente und legen die zugrunde liegende Eigenschaft der Web-Komponente fest.

$: { if (wcInstance) { wcInstance.increment = increment; }
}

Du kannst es testen vorbei an dieser Live-Demo.

Wir möchten dies natürlich nicht für jede Webkomponente oder Requisite tun, die wir verwalten müssen. Wäre es nicht schön, wenn wir uns einfach setzen könnten increment direkt in unserer Web-Komponente, im Markup, wie wir es normalerweise für Komponenten-Requisiten tun, und haben Sie es, wissen Sie, arbeite nur? Mit anderen Worten, es wäre schön, wenn wir alle Verwendungen von löschen könnten wcInstance und verwenden Sie stattdessen diesen einfacheren Code:

<counter-wc increment={increment} color={color}></counter-wc>

Es stellt sich heraus, dass wir es können. Dieser Code funktioniert; Svelte erledigt die ganze Laufarbeit für uns. Probieren Sie es in dieser Demo aus. Dies ist das Standardverhalten für so ziemlich alle JavaScript-Frameworks.

Warum habe ich Ihnen also gezeigt, wie Sie die Prop der Web-Komponente manuell festlegen? Zwei Gründe: Es ist nützlich zu verstehen, wie diese Dinge funktionieren, und vor einem Moment habe ich gesagt, dass dies für „so ziemlich“ alle JavaScript-Frameworks funktioniert. Aber es gibt ein Framework, das ärgerlicherweise keine Webkomponenten-Prop-Einstellung unterstützt, wie wir gerade gesehen haben.

React ist ein anderes Biest

Erstellen interoperabler Webkomponenten, die sogar mit React PlatoBlockchain Data Intelligence funktionieren. Vertikale Suche. Ai.
Erstellen interoperabler Webkomponenten, die sogar mit React funktionieren

Reagieren. Das beliebteste JavaScript-Framework der Welt unterstützt keine grundlegende Interoperabilität mit Webkomponenten. Dies ist ein bekanntes Problem, das nur bei React auftritt. Interessanterweise ist dies tatsächlich im experimentellen Zweig von React behoben, wurde aber aus irgendeinem Grund nicht in Version 18 integriert. Das heißt, wir können es immer noch den Fortschritt davon verfolgen. Und Sie können dies selbst mit a versuchen Live-Demo.

Die Lösung ist natürlich die Verwendung von a ref, holen Sie sich die Webkomponenteninstanz und legen Sie sie manuell fest increment wenn sich dieser Wert ändert. Es sieht aus wie das:

import React, { useState, useRef, useEffect } from 'react';
import './counter-wc'; export default function App() { const [increment, setIncrement] = useState(1); const [color, setColor] = useState('red'); const wcRef = useRef(null); useEffect(() => { wcRef.current.increment = increment; }, [increment]); return ( <div> <div className="increment-container"> <button onClick={() => setIncrement(1)}>Increment by 1</button> <button onClick={() => setIncrement(2)}>Increment by 2</button> </div> <select value={color} onChange={(e) => setColor(e.target.value)}> <option value="red">Red</option> <option value="green">Green</option> <option value="blue">Blue</option> </select> <counter-wc ref={wcRef} increment={increment} color={color}></counter-wc> </div> );
}

Wie wir besprochen haben, ist es einfach nicht skalierbar, dies manuell für jede Webkomponenteneigenschaft zu codieren. Aber es ist noch nicht alles verloren, denn wir haben ein paar Optionen.

Option 1: Attribute überall verwenden

Wir haben Attribute. Wenn Sie oben auf die React-Demo geklickt haben, wird die increment Requisite funktionierte nicht, aber die Farbe wurde korrekt geändert. Können wir nicht alles mit Attributen codieren? Traurigerweise Nein. Attributwerte können nur Zeichenfolgen sein. Das ist hier gut genug, und wir könnten mit diesem Ansatz etwas weit kommen. Zahlen wie increment kann in und aus Strings konvertiert werden. Wir könnten sogar JSON-Objekte stringifizieren/parsen. Aber irgendwann müssen wir eine Funktion an eine Webkomponente übergeben, und an diesem Punkt hätten wir keine Optionen mehr.

Option 2: Wickeln Sie es ein

Es gibt ein altes Sprichwort, dass man jedes Problem in der Informatik lösen kann, indem man eine Indirektionsebene hinzufügt (mit Ausnahme des Problems zu vieler Indirektionsebenen). Der Code zum Setzen dieser Requisiten ist ziemlich vorhersehbar und einfach. Was, wenn wir es in einer Bibliothek verstecken? Die schlauen Leute hinter Lit eine Lösung haben. Diese Bibliothek erstellt eine neue React-Komponente für Sie, nachdem Sie ihr eine Webkomponente gegeben haben, und listet die erforderlichen Eigenschaften auf. Ich bin zwar schlau, aber kein Fan dieses Ansatzes.

Anstatt eine Eins-zu-eins-Zuordnung von Webkomponenten zu manuell erstellten React-Komponenten zu haben, bevorzuge ich einfach dank One Reaktionskomponente, dass wir unsere Webkomponente übergeben Verlinke den Namen zu (counter-wc in unserem Fall) – zusammen mit allen Attributen und Eigenschaften – und damit diese Komponente unsere Webkomponente rendert, fügen Sie die hinzu ref, dann finden Sie heraus, was eine Requisite und was ein Attribut ist. Das ist meiner Meinung nach die ideale Lösung. Ich kenne keine Bibliothek, die dies tut, aber es sollte einfach zu erstellen sein. Probieren wir es aus!

Dies ist die Verwendung wir suchen nach:

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

wcTag ist der Name des Webkomponenten-Tags; der Rest sind die Eigenschaften und Attribute, die wir weitergeben wollen.

So sieht meine Implementierung aus:

import React, { createElement, useRef, useLayoutEffect, memo } from 'react'; const _WcWrapper = (props) => { const { wcTag, children, ...restProps } = props; const wcRef = useRef(null); useLayoutEffect(() => { const wc = wcRef.current; for (const [key, value] of Object.entries(restProps)) { if (key in wc) { if (wc[key] !== value) { wc[key] = value; } } else { if (wc.getAttribute(key) !== value) { wc.setAttribute(key, value); } } } }); return createElement(wcTag, { ref: wcRef });
}; export const WcWrapper = memo(_WcWrapper);

Die interessanteste Zeile ist am Ende:

return createElement(wcTag, { ref: wcRef });

So erstellen wir in React ein Element mit einem dynamischen Namen. Tatsächlich transpiliert React normalerweise JSX in dies. Alle unsere divs werden konvertiert createElement("div") Anrufe. Wir müssen diese API normalerweise nicht direkt aufrufen, aber sie ist da, wenn wir sie brauchen.

Darüber hinaus möchten wir einen Layout-Effekt ausführen und alle Requisiten durchlaufen, die wir an unsere Komponente übergeben haben. Wir durchlaufen sie alle und prüfen, ob es sich um eine Eigenschaft mit einem handelt in check , das das Instanzobjekt der Webkomponente sowie seine Prototypkette überprüft, wodurch alle Getter/Setter abgefangen werden, die auf dem Klassenprototyp landen. Wenn keine solche Eigenschaft vorhanden ist, wird davon ausgegangen, dass es sich um ein Attribut handelt. In jedem Fall setzen wir es nur, wenn sich der Wert tatsächlich geändert hat.

Wenn Sie sich fragen, warum wir verwenden useLayoutEffect statt useEffect, weil wir diese Updates sofort ausführen möchten, bevor unsere Inhalte gerendert werden. Beachten Sie auch, dass wir kein Abhängigkeitsarray zu unserem haben useLayoutEffect; Dies bedeutet, dass wir dieses Update ausführen möchten jedes rendern. Dies kann riskant sein, da React dazu neigt, neu zu rendern viel. Ich verbessere dies, indem ich das Ganze einpacke React.memo. Dies ist im Wesentlichen die moderne Version von React.PureComponent, was bedeutet, dass die Komponente nur dann erneut gerendert wird, wenn sich eine ihrer tatsächlichen Requisiten geändert hat – und sie überprüft, ob dies über eine einfache Gleichheitsprüfung geschehen ist.

Das einzige Risiko hier besteht darin, dass Sie die Aktualisierungen nicht sehen, wenn Sie eine Objektstütze übergeben, die Sie direkt mutieren, ohne sie neu zuzuweisen. Aber davon wird dringend abgeraten, besonders in der React-Community, also würde ich mir darüber keine Sorgen machen.

Bevor ich fortfahre, möchte ich noch eine letzte Sache hervorheben. Möglicherweise sind Sie mit der Verwendung nicht zufrieden. Auch diese Komponente wird wie folgt verwendet:

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

Insbesondere möchten Sie den Namen des Webkomponenten-Tags möglicherweise nicht an die übergeben <WcWrapper> Komponente und bevorzugen stattdessen die @lit-labs/react Paket oben, das für jede Webkomponente eine neue individuelle React-Komponente erstellt. Das ist absolut fair und ich würde Sie ermutigen, das zu verwenden, womit Sie sich am wohlsten fühlen. Aber für mich ist ein Vorteil dieses Ansatzes, dass es einfach ist löschen. Wenn React durch ein Wunder die richtige Handhabung von Webkomponenten aus ihrem experimentellen Zweig in React zusammenführt main Morgen könnten Sie den obigen Code von diesem ändern:

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

… dazu:

<counter-wc ref={wcRef} increment={increment} color={color} />

Sie könnten wahrscheinlich sogar einen einzigen Codemod schreiben, um das überall zu tun, und dann löschen <WcWrapper> insgesamt. Streichen Sie das eigentlich: Ein globales Suchen und Ersetzen mit einem RegEx würde wahrscheinlich funktionieren.

Die Umsetzung

Ich weiß, es scheint, als hätte es eine Reise gebraucht, um hierher zu kommen. Wenn Sie sich erinnern, bestand unser ursprüngliches Ziel darin, den Bildvorschau-Code zu übernehmen, den wir in meinem betrachtet haben Ladepfosten, und verschieben Sie es in eine Webkomponente, damit es in jedem JavaScript-Framework verwendet werden kann. Das Fehlen einer angemessenen Interop-Funktion von React fügte der Mischung viele Details hinzu. Aber jetzt, da wir einen guten Überblick darüber haben, wie man eine Webkomponente erstellt und verwendet, wird die Implementierung fast enttäuschend sein.

Ich werde die gesamte Webkomponente hier ablegen und einige der interessanten Teile hervorheben. Wenn Sie es in Aktion sehen möchten, hier ist ein Arbeitsdemo. Es wechselt zwischen meinen drei Lieblingsbüchern zu meinen drei Lieblingsprogrammiersprachen. Die URL für jedes Buch ist jedes Mal eindeutig, sodass Sie die Vorschau sehen können, obwohl Sie wahrscheinlich Dinge auf Ihrer DevTools-Netzwerkregisterkarte drosseln möchten, um wirklich zu sehen, was passiert.

Gesamten Code anzeigen
class BookCover extends HTMLElement { static observedAttributes = ['url']; attributeChangedCallback(name, oldValue, newValue) { if (name === 'url') { this.createMainImage(newValue); } } set preview(val) { this.previewEl = this.createPreview(val); this.render(); } createPreview(val) { if (typeof val === 'string') { return base64Preview(val); } else { return blurHashPreview(val); } } createMainImage(url) { this.loaded = false; const img = document.createElement('img'); img.alt = 'Book cover'; img.addEventListener('load', () =&gt; { if (img === this.imageEl) { this.loaded = true; this.render(); } }); img.src = url; this.imageEl = img; } connectedCallback() { this.render(); } render() { const elementMaybe = this.loaded ? this.imageEl : this.previewEl; syncSingleChild(this, elementMaybe); }
}

Zuerst registrieren wir das Attribut, an dem wir interessiert sind, und reagieren, wenn es sich ändert:

static observedAttributes = ['url']; attributeChangedCallback(name, oldValue, newValue) { if (name === 'url') { this.createMainImage(newValue); }
}

Dadurch wird unsere Bildkomponente erstellt, die nur beim Laden angezeigt wird:

createMainImage(url) { this.loaded = false; const img = document.createElement('img'); img.alt = 'Book cover'; img.addEventListener('load', () => { if (img === this.imageEl) { this.loaded = true; this.render(); } }); img.src = url; this.imageEl = img;
}

Als nächstes haben wir unsere Vorschau-Eigenschaft, die entweder unsere Base64-Vorschauzeichenfolge oder unsere sein kann blurhash Paket:

set preview(val) { this.previewEl = this.createPreview(val); this.render();
} createPreview(val) { if (typeof val === 'string') { return base64Preview(val); } else { return blurHashPreview(val); }
}

Dies bezieht sich auf die Hilfsfunktion, die wir benötigen:

function base64Preview(val) { const img = document.createElement('img'); img.src = val; return img;
} function blurHashPreview(preview) { const canvasEl = document.createElement('canvas'); const { w: width, h: height } = preview; canvasEl.width = width; canvasEl.height = height; const pixels = decode(preview.blurhash, width, height); const ctx = canvasEl.getContext('2d'); const imageData = ctx.createImageData(width, height); imageData.data.set(pixels); ctx.putImageData(imageData, 0, 0); return canvasEl;
}

Und zu guter Letzt unsere render Verfahren:

connectedCallback() { this.render();
} render() { const elementMaybe = this.loaded ? this.imageEl : this.previewEl; syncSingleChild(this, elementMaybe);
}

Und ein paar Hilfsmethoden, um alles zusammenzubinden:

export function syncSingleChild(container, child) { const currentChild = container.firstElementChild; if (currentChild !== child) { clearContainer(container); if (child) { container.appendChild(child); } }
} export function clearContainer(el) { let child; while ((child = el.firstElementChild)) { el.removeChild(child); }
}

Es ist ein bisschen mehr Boilerplate, als wir brauchen würden, wenn wir es in einem Framework bauen, aber der Vorteil ist, dass wir es in jedem beliebigen Framework wiederverwenden können – obwohl React, wie wir besprochen haben, vorerst einen Wrapper benötigt .

Krimskrams

Ich habe bereits den React-Wrapper von Lit erwähnt. Aber wenn Sie Stencil verwenden, unterstützt es tatsächlich a separate Ausgabepipeline nur für React. Und die guten Leute bei Microsoft haben das auch hat etwas Ähnliches wie Lits Wrapper erstellt, an die Fast-Web-Komponentenbibliothek angehängt.

Wie ich bereits erwähnt habe, übernehmen alle Frameworks, die nicht React heißen, das Festlegen von Eigenschaften von Webkomponenten für Sie. Beachten Sie nur, dass einige spezielle Syntaxvarianten haben. Beispielsweise mit Solid.js, <your-wc value={12}> setzt das immer voraus value ist eine Eigenschaft, die Sie mit einem überschreiben können attr Präfix, wie <your-wc attr:value={12}>.

Wrapping up

Webkomponenten sind ein interessanter, oft zu wenig genutzter Teil der Webentwicklungslandschaft. Sie können dazu beitragen, Ihre Abhängigkeit von einem einzelnen JavaScript-Framework zu verringern, indem sie Ihre UI- oder „Blatt“-Komponenten verwalten. Während das Erstellen dieser als Webkomponenten – im Gegensatz zu Svelte- oder React-Komponenten – nicht so ergonomisch ist, besteht der Vorteil darin, dass sie weitgehend wiederverwendbar sind.


Erstellen interoperabler Webkomponenten, die sogar mit React funktionieren ursprünglich veröffentlicht am CSS-Tricks.. Du solltest erhalten Sie den Newsletter.

Zeitstempel:

Mehr von CSS-Tricks