Tworzenie interoperacyjnych komponentów internetowych, które działają nawet z React PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Tworzenie interoperacyjnych komponentów internetowych, które działają nawet z React

Ci z nas, którzy są programistami stron internetowych od ponad kilku lat, prawdopodobnie napisali kod przy użyciu więcej niż jednego frameworka JavaScript. Przy wszystkich dostępnych opcjach — React, Svelte, Vue, Angular, Solid — jest to prawie nieuniknione. Jedną z bardziej frustrujących rzeczy, z którymi musimy się zmierzyć podczas pracy z różnymi frameworkami, jest ponowne tworzenie tych wszystkich niskopoziomowych komponentów interfejsu użytkownika: przycisków, kart, list rozwijanych itp. Szczególnie frustrujące jest to, że zazwyczaj mamy je zdefiniowane w jednym frameworku , powiedzmy React, ale potem musimy je przepisać, jeśli chcemy zbudować coś w Svelte. Lub Vue. Lub Solidny. I tak dalej.

Czy nie byłoby lepiej, gdybyśmy mogli zdefiniować te niskopoziomowe komponenty UI raz, w sposób niezależny od frameworków, a następnie używać ich ponownie między frameworkami? Oczywiście, że tak! I możemy; komponenty sieciowe są najlepszym rozwiązaniem. Ten post pokaże Ci, jak.

Jak na razie brakuje nieco historii SSR dla komponentów internetowych. Deklaratywny shadow DOM (DSD) to sposób renderowania komponentu internetowego po stronie serwera, ale w chwili pisania tego tekstu nie jest on zintegrowany z ulubionymi frameworkami aplikacji, takimi jak Next, Remix lub SvelteKit. Jeśli jest to dla Ciebie wymóg, koniecznie sprawdź najnowszy stan DSD. Ale w przeciwnym razie, jeśli SSR nie jest czymś, czego używasz, czytaj dalej.

Najpierw trochę kontekstu

Web Components to zasadniczo elementy HTML, które sam definiujesz, na przykład <yummy-pizza> czy cokolwiek, od podstaw. Są one omówione w całym CSS-Tricks (w tym obszerna seria Caleba Williamsa i jeden autorstwa Johna Rhea), ale pokrótce przejdziemy przez ten proces. Zasadniczo definiujesz klasę JavaScript, dziedziczysz ją z HTMLElement, a następnie zdefiniuj wszelkie właściwości, atrybuty i style komponentu internetowego oraz, oczywiście, znaczniki, które ostatecznie renderuje dla użytkowników.

Możliwość zdefiniowania niestandardowych elementów HTML, które nie są powiązane z żadnym konkretnym komponentem, jest ekscytująca. Ale ta wolność jest także ograniczeniem. Istniejące niezależnie od jakichkolwiek frameworków JavaScript oznacza, że ​​tak naprawdę nie możesz wchodzić w interakcje z tymi frameworkami JavaScript. Pomyśl o komponencie React, który pobiera pewne dane, a następnie je renderuje inny Komponent React, przekazujący dane. To tak naprawdę nie działałoby jako komponent sieciowy, ponieważ komponent sieciowy nie wie, jak renderować komponent React.

Komponenty internetowe szczególnie wyróżniają się jako składniki liści. Składniki liści są ostatnią rzeczą do renderowania w drzewie komponentów. Są to komponenty, które otrzymują pewne rekwizyty, a niektóre renderują UI. To są nie komponenty znajdujące się pośrodku twojego drzewa komponentów, przekazujące dane, ustawiające kontekst itp. — po prostu czyste kawałki UI będzie wyglądać tak samo, bez względu na to, który framework JavaScript obsługuje resztę aplikacji.

Komponent sieciowy, który budujemy

Zamiast budować coś nudnego (i powszechnego), jak przycisk, zbudujmy coś nieco innego. W moim załadunek stanowisko przyjrzeliśmy się użyciu niewyraźnych podglądów obrazów, aby zapobiec ponownemu wlewaniu treści i zapewnić przyzwoity interfejs użytkownika podczas ładowania naszych obrazów. Przyjrzeliśmy się kodowaniu base64 rozmytych, zdegradowanych wersji naszych obrazów i wyświetlaniu tego w naszym interfejsie użytkownika, gdy załadowany jest prawdziwy obraz. Przyjrzeliśmy się również generowaniu niezwykle kompaktowych, rozmytych podglądów za pomocą narzędzia o nazwie Rozmycie.

Ten post pokazał Ci, jak wygenerować te podglądy i wykorzystać je w projekcie React. Ten post pokaże Ci, jak korzystać z tych podglądów z komponentu internetowego, aby mogły być używane przez każdy Framework JavaScript.

Ale musimy przejść, zanim będziemy mogli biegać, więc najpierw przejdziemy przez coś trywialnego i głupiego, aby dokładnie zobaczyć, jak działają komponenty sieciowe.

Wszystko w tym poście zbuduje waniliowe komponenty internetowe bez żadnych narzędzi. Oznacza to, że kod będzie trochę schematyczny, ale powinien być stosunkowo łatwy do naśladowania. Narzędzia takie jak Świeci or Szablon są przeznaczone do budowania komponentów sieciowych i mogą być używane do usuwania większości tego schematu. Zachęcam do ich sprawdzenia! Ale w tym poście wolę trochę więcej schematu w zamian za brak konieczności wprowadzania i uczenia kolejnej zależności.

Prosty element licznika

Zbudujmy klasyczne „Hello World” z komponentów JavaScript: licznik. Wyrenderujemy wartość i przycisk zwiększający tę wartość. Proste i nudne, ale pozwoli nam przyjrzeć się najprostszemu możliwemu komponentowi webowemu.

W celu zbudowania komponentu webowego, pierwszym krokiem jest stworzenie klasy JavaScript, która dziedziczy z HTMLElement:

class Counter extends HTMLElement {}

Ostatnim krokiem jest zarejestrowanie komponentu webowego, ale tylko wtedy, gdy jeszcze go nie zarejestrowaliśmy:

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

I oczywiście wyrenderuj to:

<counter-wc></counter-wc>

A wszystko pomiędzy sprawia, że ​​komponent sieciowy robi to, co chcemy. Jedną z powszechnych metod cyklu życia jest connectedCallback, który jest uruchamiany, gdy nasz komponent sieciowy zostanie dodany do DOM. Moglibyśmy użyć tej metody do renderowania dowolnej treści. Pamiętaj, to jest klasa JS dziedzicząca z HTMLElement, co oznacza nasz this value to sam element komponentu internetowego, ze wszystkimi normalnymi metodami manipulacji DOM, które już znasz i kochasz.

Najprościej możemy to zrobić:

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

…co będzie działać dobrze.

Słowo „hej” na zielono.
Tworzenie interoperacyjnych komponentów internetowych, które działają nawet z React

Dodawanie prawdziwej treści

Dodajmy przydatne, interaktywne treści. Potrzebujemy <span> do przechowywania aktualnej wartości liczbowej i a <button> aby zwiększyć licznik. Na razie utworzymy tę treść w naszym konstruktorze i dołączymy ją, gdy komponent sieciowy faktycznie znajduje się w DOM:

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();
}

Jeśli jesteś naprawdę obrzydliwy z powodu ręcznego tworzenia DOM, pamiętaj, że możesz ustawić innerHTML, a nawet jednokrotnie utwórz element szablonu jako właściwość statyczną klasy komponentu WWW, sklonuj go i wstaw zawartość dla nowych instancji komponentu WWW. Prawdopodobnie jest kilka innych opcji, o których nie myślę, lub zawsze możesz użyć frameworka komponentów internetowych, takiego jak Świeci or Szablon. Ale w tym poście utrzymamy prostotę.

Idąc dalej, potrzebujemy ustawialnej właściwości klasy JavaScript o nazwie value

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

Jest to po prostu standardowa właściwość klasy z ustawiaczem, wraz z drugą właściwością do przechowywania wartości. Jednym z zabawnych zwrotów akcji jest to, że dla tych wartości używam składni własności prywatnej klasy JavaScript. Oznacza to, że nikt poza naszym komponentem sieciowym nie może nigdy dotknąć tych wartości. To jest standardowy JavaScript to jest obsługiwane we wszystkich nowoczesnych przeglądarkach, więc nie bój się go używać.

Lub możesz to nazwać _value Jeśli wolisz. I wreszcie nasz update metoda:

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

To działa!

Składnik sieciowy licznika.
Tworzenie interoperacyjnych komponentów internetowych, które działają nawet z React

Oczywiście nie jest to kod, który chciałbyś utrzymywać na dużą skalę. Oto pełna przykład pracy jeśli chcesz się bliżej przyjrzeć. Jak już powiedziałem, narzędzia takie jak Lit i Stencil zostały zaprojektowane, aby to uprościć.

Dodanie większej funkcjonalności

Ten post nie jest głębokim zanurzeniem się w komponenty sieciowe. Nie omówimy wszystkich interfejsów API i cykli życia; nawet nie pokryjemy cień korzenie lub szczeliny. Na te tematy jest nieskończona ilość treści. Moim celem jest zapewnienie wystarczająco przyzwoitego wprowadzenia, aby wzbudzić zainteresowanie, wraz z przydatnymi wskazówkami dotyczącymi faktycznie za pomocą komponenty internetowe z popularnymi frameworkami JavaScript, które już znasz i kochasz.

W tym celu ulepmy nieco nasz komponent sieciowy. Niech to zaakceptuje color atrybut, aby kontrolować kolor wyświetlanej wartości. I niech też to zaakceptuje i increment Właściwość, więc użytkownicy tego komponentu internetowego mogą zwiększać go o 2, 3, 4 na raz. Aby wprowadzić te zmiany stanu, użyjmy naszego nowego licznika w piaskownicy Svelte — za chwilę przejdziemy do reakcji.

Zaczniemy od tego samego komponentu internetowego co poprzednio i dodamy atrybut koloru. Aby skonfigurować nasz komponent sieciowy tak, aby akceptował i odpowiadał na atrybut, dodajemy static observedAttributes właściwość, która zwraca atrybuty, których nasłuchuje nasz komponent sieciowy.

static observedAttributes = ["color"];

Mając to na miejscu, możemy dodać attributeChangedCallback metoda cyklu życia, która zostanie uruchomiona, gdy którykolwiek z atrybutów wymienionych w observedAttributes są ustawione lub zaktualizowane.

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

Teraz aktualizujemy nasze update metoda, aby faktycznie z niego korzystać:

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

Na koniec dodajmy nasze increment własność:

increment = 1;

Prosty i skromny.

Korzystanie z licznika w Svelte

Wykorzystajmy to, co właśnie zrobiliśmy. Przejdziemy do naszego komponentu aplikacji Svelte i dodamy coś takiego:

<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>

I to działa! Nasz licznik renderuje, inkrementuje, a menu rozwijane aktualizuje kolor. Jak widać, renderujemy atrybut koloru w naszym szablonie Svelte, a gdy wartość się zmienia, Svelte zajmuje się legworkingiem setAttribute na naszym podstawowym wystąpieniu komponentu internetowego. Nie ma tu nic specjalnego: to samo, co już robi z atrybutami każdy Element HTML.

Sprawy stają się trochę interesujące z increment rekwizyt. To jest nie atrybut w naszym komponencie sieciowym; jest to właściwość klasy komponentu sieciowego. Oznacza to, że należy go ustawić w instancji komponentu internetowego. Wytrzymaj ze mną, bo za chwilę wszystko będzie o wiele prostsze.

Najpierw dodamy kilka zmiennych do naszego komponentu Svelte:

let increment = 1;
let wcInstance;

Nasz potężny komponent licznika pozwoli Ci zwiększyć o 1 lub o 2:

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

Ale, W teorii, musimy pobrać rzeczywistą instancję naszego komponentu sieciowego. To jest to samo, co zawsze robimy za każdym razem, gdy dodajemy a ref z Reactem. Z Svelte to proste bind:this dyrektywy:

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

Teraz, w naszym szablonie Svelte, nasłuchujemy zmian w zmiennej inkrementacyjnej naszego komponentu i ustawiamy podstawową właściwość komponentu sieciowego.

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

Możesz to przetestować na tym demo na żywo.

Oczywiście nie chcemy tego robić dla każdego komponentu internetowego lub właściwości, którymi musimy zarządzać. Czy nie byłoby miło, gdybyśmy mogli po prostu ustawić? increment bezpośrednio w naszym komponencie sieciowym, w znacznikach, tak jak zwykle robimy to w przypadku właściwości komponentu, i masz to, wiesz, po prostu pracuj? Innymi słowy, byłoby miło, gdybyśmy mogli usunąć wszystkie zastosowania wcInstance i zamiast tego użyj tego prostszego kodu:

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

Okazuje się, że możemy. Ten kod działa; Svelte zajmuje się za nas całą tą pracą nóg. Sprawdź to w tym demo. Jest to standardowe zachowanie dla prawie wszystkich frameworków JavaScript.

Dlaczego więc pokazałem ci ręczny sposób ustawiania właściwości komponentu webowego? Z dwóch powodów: warto zrozumieć, jak te rzeczy działają, a przed chwilą powiedziałem, że działa to „w zasadzie w przypadku” wszystkich frameworków JavaScript. Ale jest jeden framework, który, co irytujące, nie obsługuje ustawień właściwości komponentu webowego, jak właśnie widzieliśmy.

React to inna bestia

Tworzenie interoperacyjnych komponentów internetowych, które działają nawet z React PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.
Tworzenie interoperacyjnych komponentów internetowych, które działają nawet z React

Reagować. Najpopularniejszy framework JavaScript na świecie nie obsługuje podstawowego współdziałania z komponentami internetowymi. To dobrze znany problem, który występuje tylko w React. Co ciekawe, zostało to naprawione w eksperymentalnej gałęzi Reacta, ale z jakiegoś powodu nie zostało połączone z wersją 18. Mimo to nadal możemy śledź postęp tego. I możesz spróbować tego sam za pomocą Demo na żywo.

Rozwiązaniem jest oczywiście użycie a ref, chwyć instancję komponentu internetowego i ustaw ręcznie increment kiedy ta wartość się zmieni. To wygląda tak:

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> );
}

Jak już wspomnieliśmy, kodowanie tego ręcznie dla każdej właściwości komponentu internetowego po prostu nie jest skalowalne. Ale nie wszystko stracone, ponieważ mamy kilka opcji.

Opcja 1: wszędzie używaj atrybutów

Mamy atrybuty. Jeśli kliknąłeś powyższą wersję demonstracyjną React, increment rekwizyt nie działał, ale kolor zmienił się poprawnie. Czy nie możemy zakodować wszystkiego za pomocą atrybutów? Niestety nie. Wartości atrybutów mogą być tylko ciągami. To wystarczy i dzięki takiemu podejściu moglibyśmy zajść nieco daleko. Liczby takie jak increment można przekonwertować na i z ciągów. Moglibyśmy nawet stringify/parsować obiekty JSON. Ale w końcu będziemy musieli przekazać funkcję do komponentu sieciowego iw tym momencie nie mielibyśmy żadnych opcji.

Opcja 2: Zawiń to

Jest takie stare powiedzenie, że każdy problem w informatyce można rozwiązać, dodając poziom niebezpośredniości (z wyjątkiem problemu zbyt wielu poziomów niebezpośredniości). Kod do ustawienia tych rekwizytów jest dość przewidywalny i prosty. A jeśli ukryjemy to w bibliotece? Mądrzy ludzie stojący za Lit mieć jedno rozwiązanie. Ta biblioteka tworzy dla Ciebie nowy komponent React po nadaniu mu komponentu internetowego i wymienieniu potrzebnych właściwości. Choć sprytny, nie jestem fanem tego podejścia.

Zamiast mapować jeden do jednego komponentów webowych na ręcznie tworzone komponenty React, wolę po prostu pierwszej Komponent React, który przekazujemy do naszego komponentu webowego Nazwa znacznika do (counter-wc w naszym przypadku) — wraz ze wszystkimi atrybutami i właściwościami — i aby ten komponent renderował nasz komponent sieciowy, dodaj ref, a następnie dowiedz się, co jest podporą, a co atrybutem. Moim zdaniem to idealne rozwiązanie. Nie znam biblioteki, która to robi, ale tworzenie powinno być proste. Spróbujmy!

To jest zwyczaj poszukujemy:

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

wcTag to nazwa tagu komponentu internetowego; reszta to właściwości i atrybuty, które chcemy przekazać.

Oto jak wygląda moja implementacja:

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);

Najciekawsza linia znajduje się na końcu:

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

W ten sposób tworzymy w React element o dynamicznej nazwie. W rzeczywistości to jest to, do czego React zwykle transpiluje JSX. Wszystkie nasze divy są konwertowane na createElement("div") wzywa. Zwykle nie musimy wywoływać tego interfejsu API bezpośrednio, ale jest tam, kiedy go potrzebujemy.

Poza tym chcemy uruchomić efekt układu i zapętlić każdą właściwość, którą przekazaliśmy do naszego komponentu. Przechodzimy przez wszystkie z nich i sprawdzamy, czy jest to właściwość z in sprawdź, czy sprawdza obiekt instancji komponentu WWW, a także jego łańcuch prototypów, który przechwyci wszystkie gettery/settery, które kończą się na prototypie klasy. Jeśli taka właściwość nie istnieje, zakłada się, że jest to atrybut. W obu przypadkach ustawiamy go tylko wtedy, gdy wartość faktycznie się zmieniła.

Jeśli zastanawiasz się, dlaczego używamy useLayoutEffect zamiast useEffect, ponieważ chcemy natychmiast uruchomić te aktualizacje przed wyrenderowaniem naszej zawartości. Zauważ też, że nie mamy tablicy zależności do naszego useLayoutEffect; oznacza to, że chcemy uruchomić tę aktualizację każdy render. Może to być ryzykowne, ponieważ React ma tendencję do ponownego renderowania dużo. Poprawiam to, owijając całość w React.memo. Jest to zasadniczo nowoczesna wersja React.PureComponent, co oznacza, że ​​komponent zostanie ponownie wyrenderowany tylko wtedy, gdy zmieni się którakolwiek z jego rzeczywistych właściwości — i sprawdza, czy tak się stało, za pomocą prostego sprawdzenia równości.

Jedynym ryzykiem jest to, że jeśli przekazujesz obiekt, który mutujesz bezpośrednio bez ponownego przypisywania, nie zobaczysz aktualizacji. Ale jest to bardzo odradzane, szczególnie w społeczności React, więc nie martwiłbym się tym.

Zanim przejdę dalej, chciałbym powiedzieć ostatnią rzecz. Możesz nie być zadowolony z tego, jak wygląda użytkowanie. Ponownie, ten składnik jest używany w następujący sposób:

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

W szczególności może nie spodobać Ci się przekazywanie nazwy tagu komponentu internetowego do <WcWrapper> składnik i zamiast tego wolą @lit-labs/react powyżej, który tworzy nowy, indywidualny komponent React dla każdego komponentu sieciowego. To całkowicie sprawiedliwe i zachęcam do korzystania z tego, z czym czujesz się najbardziej komfortowo. Ale dla mnie jedną z zalet takiego podejścia jest to, że łatwo jest usunąć. Jeśli jakimś cudem React połączy odpowiednią obsługę komponentów sieciowych z ich eksperymentalnej gałęzi w main jutro będziesz mógł zmienić powyższy kod z tego:

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

…do tego:

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

Prawdopodobnie mógłbyś nawet napisać jednego codemoda, który zrobi to wszędzie, a potem usunąć <WcWrapper> całkowicie. Właściwie to zdrap: globalne wyszukiwanie i zastępowanie za pomocą RegEx prawdopodobnie zadziała.

Implementacja

Wiem, wygląda na to, że dotarcie tutaj zajęło mi podróż. Jeśli pamiętasz, naszym pierwotnym celem było pobranie kodu podglądu obrazu, który przyjrzeliśmy się w my załadunek stanowiskoi przenieś go do komponentu internetowego, aby można go było używać w dowolnej strukturze JavaScript. Brak odpowiedniego współdziałania Reacta dodał wiele szczegółów do miksu. Ale teraz, kiedy mamy przyzwoitą wiedzę na temat tworzenia komponentu internetowego i korzystania z niego, implementacja będzie prawie anty-klimatyczna.

Wrzucę tutaj cały komponent sieciowy i wymienię kilka interesujących fragmentów. Jeśli chcesz zobaczyć to w akcji, oto demo pracy. Będzie przełączać się między moimi trzema ulubionymi książkami o trzech ulubionych językach programowania. Adres URL każdej książki będzie za każdym razem niepowtarzalny, więc możesz zobaczyć podgląd, chociaż prawdopodobnie będziesz chciał ograniczyć rzeczy na karcie DevTools Network, aby naprawdę zobaczyć, co się dzieje.

Zobacz cały kod
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); }
}

Najpierw rejestrujemy interesujący nas atrybut i reagujemy, gdy się zmieni:

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

Powoduje to utworzenie naszego komponentu obrazu, który pokaże się tylko po załadowaniu:

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;
}

Następnie mamy naszą właściwość Preview, która może być naszym ciągiem podglądu base64 lub naszym blurhash paczka:

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

Odnosi się to do dowolnej funkcji pomocniczej, której potrzebujemy:

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;
}

I wreszcie nasz render metoda:

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

I kilka pomocniczych metod, aby wszystko powiązać:

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); }
}

To trochę więcej schematu, niż byśmy potrzebowali, gdybyśmy zbudowali to we frameworku, ale zaletą jest to, że możemy ponownie użyć tego w dowolnym frameworku, jaki tylko zechcemy — chociaż React będzie potrzebował na razie wrappera, o czym mówiliśmy .

Szanse i cele

Wspomniałem już o opakowaniu Lit's React. Ale jeśli okaże się, że używasz Stencila, faktycznie obsługuje on oddzielny potok wyjściowy tylko dla React. Dobrzy ludzie z Microsoftu też mają stworzył coś podobnego do wrappera Lit, dołączony do biblioteki komponentów Fast web.

Jak wspomniałem, wszystkie frameworki, które nie są nazwane React, zajmą się ustawianiem właściwości komponentów webowych za Ciebie. Pamiętaj tylko, że niektóre mają specjalne smaki składni. Na przykład z Solid.js, <your-wc value={12}> zawsze zakłada, że value to właściwość, którą można nadpisać za pomocą attr prefiks, jak <your-wc attr:value={12}>.

Zamykając

Komponenty sieciowe są interesującą, często niedostatecznie wykorzystywaną częścią krajobrazu tworzenia stron internetowych. Mogą pomóc w zmniejszeniu zależności od dowolnej pojedynczej struktury JavaScript, zarządzając interfejsem użytkownika lub komponentami typu „leaf”. Chociaż tworzenie ich jako komponentów internetowych — w przeciwieństwie do komponentów Svelte lub React — nie będzie tak ergonomiczne, zaletą jest to, że będą one szeroko wielokrotnego użytku.


Tworzenie interoperacyjnych komponentów internetowych, które działają nawet z React pierwotnie opublikowany w dniu Sztuczki CSS. Powinieneś pobierz biuletyn.

Znak czasu:

Więcej z Sztuczki CSS