Bygga interoperabla webbkomponenter som till och med fungerar med React PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.

Bygga interoperabla webbkomponenter som till och med fungerar med React

De av oss som har varit webbutvecklare i mer än några år har förmodligen skrivit kod med mer än ett JavaScript-ramverk. Med alla val som finns - React, Svelte, Vue, Angular, Solid - är det nästan oundvikligt. En av de mer frustrerande sakerna vi måste ta itu med när vi arbetar över ramverk är att återskapa alla dessa lågnivåkomponenter i användargränssnittet: knappar, flikar, rullgardinsmenyer, etc. Det som är särskilt frustrerande är att vi vanligtvis har dem definierade i ett ramverk. , säg React, men behöver sedan skriva om dem om vi vill bygga något i Svelte. Eller Vue. Eller Solid. Och så vidare.

Skulle det inte vara bättre om vi kunde definiera dessa lågnivåkomponenter i användargränssnittet en gång, på ett ramagnostiskt sätt, och sedan återanvända dem mellan ramverk? Självklart skulle det! Och vi kan; webbkomponenter är vägen. Det här inlägget kommer att visa dig hur.

Från och med nu är SSR-historien för webbkomponenter lite bristfällig. Declarative shadow DOM (DSD) är hur en webbkomponent renderas på serversidan, men när detta skrivs är den inte integrerad med dina favoritapplikationsramverk som Next, Remix eller SvelteKit. Om det är ett krav för dig, se till att kontrollera den senaste statusen för DSD. Men annars, om SSR inte är något du använder, läs vidare.

Först lite sammanhang

Webbkomponenter är i huvudsak HTML-element som du definierar själv, till exempel <yummy-pizza> eller vad som helst, från grunden. De täcks överallt här på CSS-Tricks (inklusive en omfattande serie av Caleb Williams och en av John Rhea) men vi går kort igenom processen. I huvudsak definierar du en JavaScript-klass, ärver den från HTMLElement, och definiera sedan vilka egenskaper, attribut och stilar som webbkomponenten har och, naturligtvis, uppmärkningen den i slutändan kommer att ge dina användare.

Att kunna definiera anpassade HTML-element som inte är bundna till någon speciell komponent är spännande. Men denna frihet är också en begränsning. Att existera oberoende av något JavaScript-ramverk innebär att du inte riktigt kan interagera med dessa JavaScript-ramverk. Tänk på en React-komponent som hämtar en del data och sedan renderar en del andra Reagera komponent, vidarebefordra data. Detta skulle egentligen inte fungera som en webbkomponent, eftersom en webbkomponent inte vet hur man renderar en React-komponent.

Webbkomponenter utmärker sig särskilt som bladkomponenter. Bladkomponenter är det sista som ska renderas i ett komponentträd. Det här är komponenterna som får en del rekvisita och återger en del UI. Dessa är inte komponenterna som sitter i mitten av ditt komponentträd, förmedlar data, ställer in sammanhang, etc. — bara rena bitar av UI som kommer att se likadant ut, oavsett vilket JavaScript-ramverk som driver resten av appen.

Webbkomponenten vi bygger

Istället för att bygga något tråkigt (och vanligt), som en knapp, låt oss bygga något lite annorlunda. I min senaste inlägget vi tittade på att använda suddiga bildförhandsvisningar för att förhindra innehållsåterflöde och tillhandahålla ett anständigt användargränssnitt för användarna medan våra bilder laddas. Vi tittade på base64 som kodade en suddiga, försämrade versioner av våra bilder och visade det i vårt användargränssnitt medan den verkliga bilden laddades. Vi tittade också på att skapa otroligt kompakta, suddiga förhandsvisningar med hjälp av ett verktyg som heter Blurhash.

Det inlägget visade dig hur du genererar dessa förhandsvisningar och använder dem i ett React-projekt. Det här inlägget kommer att visa dig hur du använder dessa förhandsvisningar från en webbkomponent så att de kan användas av vilken som helst JavaScript-ramverk.

Men vi måste gå innan vi kan springa, så vi går igenom något trivialt och dumt först för att se exakt hur webbkomponenter fungerar.

Allt i det här inlägget kommer att bygga vaniljwebbkomponenter utan några verktyg. Det betyder att koden kommer att ha lite mönster, men bör vara relativt lätt att följa. Verktyg som Lit or Stencil är designade för att bygga webbkomponenter och kan användas för att ta bort mycket av denna pannplåt. Jag uppmanar dig att kolla in dem! Men för det här inlägget kommer jag att föredra lite mer bottenplatta i utbyte mot att jag inte behöver introducera och lära ut ett annat beroende.

En enkel diskkomponent

Låt oss bygga den klassiska "Hello World" av JavaScript-komponenter: en räknare. Vi återger ett värde och en knapp som ökar det värdet. Enkelt och tråkigt, men det låter oss titta på den enklaste möjliga webbkomponenten.

För att bygga en webbkomponent är det första steget att göra en JavaScript-klass, som ärver från HTMLElement:

class Counter extends HTMLElement {}

Det sista steget är att registrera webbkomponenten, men bara om vi inte redan har registrerat den:

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

Och, naturligtvis, återge det:

<counter-wc></counter-wc>

Och allt däremellan är att vi får webbkomponenten att göra vad vi vill att den ska. En vanlig livscykelmetod är connectedCallback, som aktiveras när vår webbkomponent läggs till i DOM. Vi kan använda den metoden för att återge vilket innehåll vi vill. Kom ihåg att detta är en JS-klass som ärver från HTMLElement, vilket betyder vår this värde är själva webbkomponentelementet, med alla normala DOM-manipulationsmetoder du redan känner till och älskar.

Som enklast kan vi göra så här:

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

...vilket kommer att fungera bra.

Ordet "hej" i grönt.
Bygga interoperabla webbkomponenter som till och med fungerar med React

Lägger till riktigt innehåll

Låt oss lägga till lite användbart, interaktivt innehåll. Vi behöver en <span> för att hålla det aktuella siffervärdet och a <button> för att öka räknaren. För närvarande skapar vi detta innehåll i vår konstruktor och lägger till det när webbkomponenten faktiskt finns i 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();
}

Om du verkligen blir trött på det manuella DOM-skapandet, kom ihåg att du kan ställa in innerHTML, eller till och med skapa ett mallelement en gång som en statisk egenskap för din webbkomponentklass, klona den och infoga innehållet för nya webbkomponentinstanser. Det finns förmodligen några andra alternativ jag inte tänker på, eller så kan du alltid använda ett ramverk för webbkomponenter som Lit or Stencil. Men för det här inlägget fortsätter vi att hålla det enkelt.

När vi går vidare behöver vi en inställbar JavaScript-klassegenskap med namnet value

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

Det är bara en egenskap i standardklass med en sätter, tillsammans med en andra egenskap för att hålla värdet. En rolig twist är att jag använder egendomssyntaxen för den privata JavaScript-klassen för dessa värden. Det betyder att ingen utanför vår webbkomponent någonsin kan röra dessa värden. Detta är standard JavaScript som stöds i alla moderna webbläsare, så var inte rädd för att använda den.

Eller ring det gärna _value om du föredrar. Och slutligen vår update metod:

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

Det fungerar!

Räknarwebbkomponenten.
Bygga interoperabla webbkomponenter som till och med fungerar med React

Uppenbarligen är detta inte kod du vill behålla i skala. Här är en full arbetsexempel om du vill ha en närmare titt. Som jag har sagt är verktyg som Lit och Stencil designade för att göra detta enklare.

Lägger till lite mer funktionalitet

Det här inlägget är inte en djupdykning i webbkomponenter. Vi kommer inte att täcka alla API:er och livscykler; vi täcker inte ens skuggrötter eller slitsar. Det finns oändligt med innehåll om dessa ämnen. Mitt mål här är att ge en tillräckligt bra introduktion för att väcka ett visst intresse, tillsammans med lite användbar vägledning om faktiskt med hjälp av webbkomponenter med de populära JavaScript-ramverken som du redan känner till och älskar.

Låt oss därför förbättra vår motwebbkomponent lite. Låt oss få det att acceptera en color attribut, för att styra färgen på värdet som visas. Och låt oss också få det att acceptera en increment egendom, så att konsumenter av den här webbkomponenten kan öka den med 2, 3, 4 åt gången. Och för att driva dessa tillståndsförändringar, låt oss använda vår nya räknare i en Svelte-sandlåda — vi kommer till React om lite.

Vi börjar med samma webbkomponent som tidigare och lägger till ett färgattribut. För att konfigurera vår webbkomponent att acceptera och svara på ett attribut lägger vi till en statisk observedAttributes egenskap som returnerar de attribut som vår webbkomponent lyssnar efter.

static observedAttributes = ["color"];

Med det på plats kan vi lägga till en attributeChangedCallback livscykelmetod, som kommer att köras när någon av attributen som anges i observedAttributes är inställda eller uppdaterade.

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

Nu uppdaterar vi vår update metod för att faktiskt använda det:

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

Till sist, låt oss lägga till vår increment fast egendom:

increment = 1;

Enkel och ödmjuk.

Använder räknarkomponenten i Svelte

Låt oss använda det vi nyss gjorde. Vi går in i vår Svelte-appkomponent och lägger till något sånt här:

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

Och det fungerar! Vår räknare renderar, ökar och rullgardinsmenyn uppdaterar färgen. Som du kan se återger vi färgattributet i vår Svelte-mall och, när värdet ändras, hanterar Svelte benarbetet med att ringa setAttribute på vår underliggande webbkomponentinstans. Det finns inget speciellt här: det här är samma sak som det redan gör för attributen för vilken som helst HTML-element.

Saker och ting blir lite intressanta med increment stötta. Detta är inte ett attribut på vår webbkomponent; det är en rekvisita på webbkomponentens klass. Det betyder att den måste ställas in på webbkomponentens instans. Håll ut med mig, det kommer att bli mycket enklare om ett tag.

Först lägger vi till några variabler till vår Svelte-komponent:

let increment = 1;
let wcInstance;

Vår kraftpaket med en räknarkomponent låter dig öka med 1 eller 2:

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

Men, i teorinmåste vi få den faktiska instansen av vår webbkomponent. Det här är samma sak som vi alltid gör när vi lägger till en ref med React. Med Svelte är det enkelt bind:this direktiv:

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

Nu, i vår Svelte-mall, lyssnar vi efter ändringar i vår komponents inkrementvariabel och ställer in den underliggande webbkomponentegenskapen.

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

Du kan testa det över på den här livedemon.

Vi vill uppenbarligen inte göra detta för varje webbkomponent eller rekvisita vi behöver hantera. Skulle det inte vara trevligt om vi bara kunde ställa in increment direkt på vår webbkomponent, i uppmärkning, som vi normalt gör för komponentrekvisita, och har det, du vet, bara arbeta? Med andra ord skulle det vara trevligt om vi kunde ta bort all användning av wcInstance och använd den här enklare koden istället:

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

Det visar sig att vi kan. Denna kod fungerar; Svelte sköter allt det där benarbetet åt oss. Kolla in det i denna demo. Detta är standardbeteende för i stort sett alla JavaScript-ramverk.

Så varför visade jag dig det manuella sättet att ställa in webbkomponentens rekvisita? Två anledningar: det är användbart att förstå hur dessa saker fungerar och för ett ögonblick sedan sa jag att detta fungerar för "i stort sett" alla JavaScript-ramverk. Men det finns ett ramverk som, förvånansvärt nog, inte stöder inställning av webbkomponentpropeller som vi just såg.

React är ett annat odjur

Bygga interoperabla webbkomponenter som till och med fungerar med React PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.
Bygga interoperabla webbkomponenter som till och med fungerar med React

Reagera. Det mest populära JavaScript-ramverket på planeten stöder inte grundläggande interop med webbkomponenter. Detta är ett välkänt problem som är unikt för React. Intressant nog är detta faktiskt fixat i Reacts experimentgren, men slogs av någon anledning inte ihop till version 18. Som sagt, vi kan fortfarande spåra utvecklingen av det. Och du kan prova detta själv med en demo.

Lösningen är naturligtvis att använda en ref, ta tag i webbkomponentinstansen och ställ in manuellt increment när det värdet ändras. Det ser ut så här:

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

Som vi diskuterade är det helt enkelt inte skalbart att koda upp detta manuellt för varje webbkomponentegenskap. Men allt är inte förlorat eftersom vi har ett par alternativ.

Alternativ 1: Använd attribut överallt

Vi har attribut. Om du klickade på React-demon ovan, increment prop fungerade inte, men färgen ändrades korrekt. Kan vi inte koda allt med attribut? Tyvärr nej. Attributvärden kan bara vara strängar. Det är tillräckligt bra här, och vi skulle kunna komma något långt med detta tillvägagångssätt. Siffror som increment kan konverteras till och från strängar. Vi kunde till och med JSON strängifiera/tolka objekt. Men så småningom kommer vi att behöva överföra en funktion till en webbkomponent, och vid den tidpunkten skulle vi inte ha några alternativ.

Alternativ 2: Slå in den

Det finns ett gammalt talesätt som säger att du kan lösa alla problem inom datavetenskap genom att lägga till en nivå av inriktning (förutom problemet med för många nivåer av inriktning). Koden för att ställa in dessa rekvisita är ganska förutsägbar och enkel. Tänk om vi gömmer det i ett bibliotek? De smarta människorna bakom Lit har en lösning. Det här biblioteket skapar en ny React-komponent åt dig efter att du har gett den en webbkomponent och listar de egenskaper den behöver. Även om jag är smart, är jag inte ett fan av detta tillvägagångssätt.

Istället för att ha en en-till-en-mappning av webbkomponenter till manuellt skapade React-komponenter, är det jag föredrar bara ett Reagera komponent som vi passerar vår webbkomponent taggnamn till (counter-wc i vårt fall) – tillsammans med alla attribut och egenskaper – och för att denna komponent ska återge vår webbkomponent, lägg till ref, ta reda på vad som är en rekvisita och vad som är ett attribut. Det är den idealiska lösningen enligt mig. Jag känner inte till ett bibliotek som gör detta, men det borde vara enkelt att skapa. Låt oss ge det ett försök!

Detta är den användning vi letar efter:

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

wcTag är webbkomponentens taggnamn; resten är de egenskaper och attribut vi vill ha vidarebefordrat.

Så här ser min implementering ut:

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

Den mest intressanta raden är i slutet:

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

Så här skapar vi ett element i React med ett dynamiskt namn. I själva verket är det detta som React normalt transpilerar JSX till. Alla våra div är konverterade till createElement("div") samtal. Vi behöver normalt inte anropa detta API direkt men det finns där när vi behöver det.

Utöver det vill vi köra en layouteffekt och gå igenom varje rekvisita som vi har skickat till vår komponent. Vi går igenom alla och kollar om det är en fastighet med en in check that kontrollerar webbkomponentens instansobjekt såväl som dess prototypkedja, vilket kommer att fånga alla getters/setters som hamnar på klassprototypen. Om det inte finns någon sådan egenskap antas det vara ett attribut. I båda fallen ställer vi bara in det om värdet faktiskt har ändrats.

Om du undrar varför vi använder useLayoutEffect istället för useEffect, det beror på att vi vill köra dessa uppdateringar omedelbart innan vårt innehåll renderas. Observera också att vi inte har någon beroendematris till vår useLayoutEffect; det betyder att vi vill köra den här uppdateringen på varje rendering. Detta kan vara riskabelt eftersom React tenderar att återrendera mycket. Jag förbättrar detta genom att slå in det hela React.memo. Detta är i huvudsak den moderna versionen av React.PureComponent, vilket innebär att komponenten bara återskapas om någon av dess faktiska rekvisita har ändrats — och den kontrollerar om det har hänt via en enkel jämställdhetskontroll.

Den enda risken här är att om du skickar en objektprop som du muterar direkt utan att omtilldela, kommer du inte att se uppdateringarna. Men detta är mycket avskräckt, särskilt i React-communityt, så jag skulle inte oroa mig för det.

Innan jag går vidare skulle jag vilja ropa ut en sista sak. Du kanske inte är nöjd med hur användningen ser ut. Återigen, den här komponenten används så här:

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

Specifikt kanske du inte gillar att skicka webbkomponenttaggnamnet till <WcWrapper> komponent och föredrar istället @lit-labs/react paketet ovan, vilket skapar en ny individuell React-komponent för varje webbkomponent. Det är helt rättvist och jag skulle uppmuntra dig att använda det du är mest bekväm med. Men för mig är en fördel med detta tillvägagångssätt att det är lätt att radera. Om genom något mirakel React slår samman korrekt webbkomponenthantering från sin experimentgren till main imorgon skulle du kunna ändra ovanstående kod från detta:

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

…till detta:

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

Du kan förmodligen till och med skriva en enda codemod för att göra det överallt och sedan ta bort <WcWrapper> sammanlagt. Faktiskt, repa det: en global sökning och ersätt med ett RegEx skulle förmodligen fungera.

Genomförandet

Jag vet, det verkar som om det tog en resa att komma hit. Om du kommer ihåg var vårt ursprungliga mål att ta förhandsgranskningskoden som vi tittade på i min senaste inlägget, och flytta den till en webbkomponent så att den kan användas i alla JavaScript-ramverk. Reacts brist på korrekt interop gav många detaljer till mixen. Men nu när vi har ett hyfsat grepp om hur man skapar en webbkomponent, och använder den, kommer implementeringen nästan att vara antiklimaktisk.

Jag släpper hela webbkomponenten här och ropar ut några av de intressanta bitarna. Om du vill se den i aktion, här är en fungerande demo. Den kommer att växla mellan mina tre favoritböcker på mina tre favoritprogrammeringsspråk. Webbadressen för varje bok kommer att vara unik varje gång, så att du kan se förhandsgranskningen, även om du förmodligen vill strypa saker på fliken DevTools Network för att verkligen se saker som händer.

Se hela koden
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); }
}

Först registrerar vi attributet vi är intresserade av och reagerar när det ändras:

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

Detta gör att vår bildkomponent skapas, som endast visas när den laddas:

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

Därefter har vi vår förhandsgranskningsegenskap, som antingen kan vara vår base64-förhandsgranskningssträng eller vår 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); }
}

Detta avser vilken hjälpfunktion vi än behöver:

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

Och slutligen vår render metod:

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

Och några hjälpmetoder för att knyta ihop allt:

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

Det är lite mer platt än vi skulle behöva om vi bygger det här i ett ramverk, men fördelen är att vi kan återanvända det här i alla ramar vi vill – även om React kommer att behöva en omslag för nu, som vi diskuterade .

Småsaker

Jag har redan nämnt Lit's React-omslag. Men om du kommer på dig själv med att använda Stencil, stöder den faktiskt en separat utgående pipeline bara för React. Och det har de goda på Microsoft också skapat något som liknar Lits omslag, ansluten till Fast web-komponentbiblioteket.

Som jag nämnde kommer alla ramverk som inte heter React att hantera att ställa in webbkomponentegenskaper åt dig. Observera bara att vissa har några speciella smaker av syntax. Till exempel, med Solid.js, <your-wc value={12}> utgår alltid från det value är en fastighet, som du kan åsidosätta med en attr prefix, som <your-wc attr:value={12}>.

Inslagning upp

Webbkomponenter är en intressant, ofta underutnyttjad del av webbutvecklingslandskapet. De kan hjälpa till att minska ditt beroende av ett enskilt JavaScript-ramverk genom att hantera ditt användargränssnitt eller "blad"-komponenter. Även om det inte kommer att vara så ergonomiskt att skapa dessa som webbkomponenter – i motsats till Svelte- eller React-komponenter – är fördelen att de kommer att kunna återanvändas i stor utsträckning.


Bygga interoperabla webbkomponenter som till och med fungerar med React ursprungligen publicerad på CSS-tricks. Du borde få nyhetsbrevet.

Tidsstämpel:

Mer från CSS-tricks