Die Schweiz verfolgt einen funktionalen Ansatz für Webkomponenten mit Preact mit Shadow DOM für Style-Kapselung, benutzerdefinierte Elemente für die Interoperabilität und das serverseitige Rendering für Universalität.
Garn : yarn add switzerland
NPM : npm install switzerland
CDN : https://cdn.jsdelivr.net/npm/switzerland@latest/dist/index.client.js
Die Schweiz beginnt optional mit dem serverseitigen Rendering mit der Flüssigkeitszufuhr auf dem Client dank deklarativer Schattendom-mit unseren Komponenten, die aufgrund unserer Vorwirkung sehr vertraut aussehen.
import { create } from "switzerland" ;
export default create ( "x-countries" , ( ) => {
return (
< ul >
< li > Japan </ li >
< li > Croatia </ li >
< li > Singapore </ li >
</ ul >
) ;
} ) ; Sobald wir unsere x-countries -Komponente definiert haben, können wir sie sowohl auf dem Server rendern als auch als Standard-Element <x-countries /> DOM auf dem Client fydratieren. Wir können dann noch einen Schritt weiter gehen und unsere Länder als HTML-Attribut auf dem DOM-Knoten unter Verwendung von <x-countries list="Japan,Croatia,Singapore"> geben.
import { create , type , use } from "switzerland" ;
type Attrs = {
countries : string [ ] ;
} ;
export default create < Attrs > ( "x-countries" , ( ) => {
const attrs = use . attrs ( {
countries : type . Array ( type . String ) ,
} ) ;
return (
< ul >
{ attrs . countries . map ( ( country ) => (
< li key = { country } > { country } </ li >
) ) }
</ ul >
) ;
} ) ; Um unsere Komponente aus einer Knotenumgebung zu verwenden, müssen wir die exportierte asynchrone render verwenden. Wir können einen optionalen zweiten Parameter für die Funktion angeben. Unsere Komponente führt jedoch derzeit keine Datenabfertigung oder Medieneinbeziehung durch und ist daher unnötig.
import { render } from "switzerland" ;
app . get ( "/" , async ( _ , response ) => {
const html = await render ( < Countries list = "Japan,Croatia,Singapore" /> ) ;
response . send ( html ) ;
} ) ;Da unsere Komponenten in sich geschlossene Module sind, führen alle Änderungen an ihren Attributen einen Neurender des Baumes der Komponente ein-unabhängig davon, ob sich diese Attribute von einer anderen Komponente oder über Vanille-Dom-Accessors ändern.
const node = document . querySelector ( "x-countries" ) ;
node . attributes . values = ` ${ node . attributes . values } ,Ukraine,Maldives` ; Die Schweiz muss nicht kompiliert werden, außer für optionale Typscript- und JSX -Transporation, da native ES -Module im Browser und Node 16+ auf dem Server verwendet werden. Dies erreicht dies durch die Verwendung node_modules beim Rendern auf dem Server mit den benannten Importen. Im Browser werden importieren Karten verwendet, um die benannten Importe für CDN -URLs zu beheben, die erweitertes Caching bieten. Wir bieten ein Dienstprogramm für den Server, um die Importkarten für Ihre Anwendung basierend auf seinen Abhängigkeiten automatisch zu generieren.
import fs from "node:fs" ;
import { imports , render } from "switzerland" ;
app . get ( "/" , async ( _ , response ) => {
const html = await render ( < Countries list = "Japan,Croatia,Singapore" /> ) ;
const importMap = await imports ( { path : path . resolve ( "../app/src" ) } ) ;
response . send ( `
<head>
<script type="importmap">
${ importMap }
</script>
</head>
<body>
${ html }
</body>
` ) ;
} ) ; Sie müssen die imports den Grundweg Ihrer Schweizkomponenten geben. Anschließend durchquert die Dateien mit ts-morph , das einen abstrakten Syntaxbaum (AST) Ihres Codes bietet und es uns ermöglicht, die externen Abhängigkeiten auszuwählen. Anschließend stimmt es iterativ mit jedem dieser Abhängigkeiten überein, die es für die von Ihrem ausgewählten Paketmanager installierten Versionen findet. Wir verwenden das @jspm/generator -Paket, um die Abhängigkeiten standardmäßig an jspm.io -URLs zu beheben. Sie können jedoch auch die provider übergeben, um den Anbieter zu ändern.
Sobald Sie die Importkarte konfiguriert haben, wird bei der Rendern der Schweizkomponenten im Browser diese CDN -URLs verwendet und verhindern, dass Abhängigkeiten über einen Bundler einpacken. Sie können sich nur auf die einfache Aufgabe konzentrieren, Typscript und JSX in native ES -Module mit nichts weiter als tsc umzuwandeln. Wenn Sie jedoch miniieren möchten, müssen Sie möglicherweise einen Terser hinzufügen.
{
"include" : [ " src " ],
"compilerOptions" : {
"rootDir" : " ./src " ,
"outDir" : " ./dist " ,
"module" : " esnext " ,
"moduleResolution" : " nodenext " ,
"esModuleInterop" : true ,
"target" : " esnext " ,
"strict" : true ,
"jsx" : " react-jsx " ,
"jsxImportSource" : " preact " ,
"declaration" : true
}
}Da wir Preact verwenden, um die Komponenten der Schweiz zu rendern, sollte die API bereits bekannt sein. Um die Benutzerfreundlichkeit zu vereinfachen, exportieren wir die Hook-Funktionen von Preact erneut, aber Sie können sie auch direkt von Preact verwenden.
import { create , use } from "switzerland" ;
export default create ( "x-countries" , ( ) => {
const [ countries , setCountries ] = use . state ( [
"Japan" ,
"Croatia" ,
"Singapore" ,
] ) ;
return (
< ul >
{ countries . map ( ( country ) => (
< li key = { country } > { country } </ li >
) ) }
</ ul >
) ;
} ) ; Stile innerhalb einer Schattengrenze ermöglichen eine Kapselung, was bedeutet, dass wir regelmäßige CSS -Dokumente verwenden können, die an den Baum unserer Komponente gesammelt werden. Mithilfe eines regulären link können wir unsere Stylesheets an unsere Komponente anhängen, obwohl die Schweiz ein node -Dienstprogramm für StyleSheet und Variables bietet. Letztere wendet benutzerdefinierte Variablen auf Ihren Komponentenbaum an, sodass CSS auf diese JavaScript -Variablen zugreifen kann. Wir verwenden den use.path -Hook, um Medien zu beheben - CSS -Dokumente, Bilder usw. - relativ zu unserer Komponente.
import { create , node , use } from "switzerland" ;
export default create ( "x-countries" , ( ) => {
const path = use . path ( import . meta . url ) ;
const [ countries , setCountries ] = use . state ( [
"Japan" ,
"Croatia" ,
"Singapore" ,
] ) ;
return (
< >
< ul >
{ countries . map ( ( country ) => (
< li key = { country } > { country } </ li >
) ) }
</ ul >
< node . Variables
backgroundColour = { countries . length === 0 ? "#8ECCD4" : "#FBDEA3" }
/>
< node . StyleSheet href = { path ( "./styles/default.css" ) } />
< node . StyleSheet
href = { path ( "./styles/mobile.css" ) }
media = "(max-width: 768px)"
/>
< node . StyleSheet href = { path ( "./styles/print.css" ) } media = "print" />
</ >
) ;
} ) ;Wir können dann ziemlich locker sein, wenn wir diese Stile auf unsere Komponente anwenden, wenn wir wissen, dass die Schattengrenze verhindern wird, dass Stile austreten. Wir verwenden eine CSS -Variable, um eine bedingte Hintergrundfarbe mit einem Fallback anzuwenden.
: host {
box - shadow : 0 0 5px # e8c5b0;
}
ul {
background - col or : var( --background-color , "#E39AC7" );
} Da die Schweiz das serverseitige Rendering standardmäßig eine use.loader ermöglicht . Der Lader Utility Hook wird useEffect Abrufen von Daten bereitgestellt. Durch die Verwendung loader -Hook können die Datenserver-Seite abgerufen und dann eine erneute Förderung des Clients verhindern. Wir erreichen dies, indem wir unsere Komponenten zweimal in der asynchronen render , die wir zuvor behandelt haben, und dann die serialisierten Daten in den Baum rendern.
import { create , use } from "switzerland" ;
export default create ( "x-countries" , ( ) => {
const { data , loading , error } = use . loader (
"x-countries" ,
( ) =>
fetch ( "https://www.example.org/countries" ) . then ( ( response ) =>
response . json ( )
) ,
null
) ;
return loading ? (
< p > Loading… </ p >
) : (
< ul >
{ data . map ( ( country ) => (
< li key = { country } > { country } </ li >
) ) }
</ ul >
) ;
} ) ; Wir bieten der loader -Funktion eine eindeutige ID an, die die individuelle Anfrage identifizieren sollte, um Duplikate zu verhindern und eine Abstimmung über den Kunden zu ermöglichen. Mit dem Abhängigkeitsargument in der dritten Position können wir die loader Client-Seite wieder aufnehmen, wenn sich ein Parameter ändert. In unserem Fall möchten wir wahrscheinlich nicht erneut geändert werden, aber wenn Sie nach einer bestimmten Liste abrufen, erwarten wir möglicherweise, dass die aktuelle Liste der Länder als Abhängigkeiten bereitgestellt wird.
Für die Bereitstellung des Umgebungskontexts erfordert eine gewisse Benutzerkonfiguration auf der Serverseite-die Funktion render nimmt einen optionalen zweiten Parameter vor, mit dem wir sowohl das Root-Verzeichnis im Webserver als auch die Domäne, auf der wir den Server ausführen, angeben können.
import App from "./App" ;
import { preload , render } from "switzerland" ;
const vendor = path . resolve ( ".." ) ;
const options = {
path : process . env [ "DOMAIN" ]
? `https:// ${ process . env [ "DOMAIN" ] } /client`
: "http://localhost:3000/client" ,
root : vendor ,
} ;
app . get ( "/" , async ( _ , response ) => {
const html = await render (
< Countries list = "Japan,Croatia,Singapore" / > ,
options
) ;
response . send ( html ) ;
} ) ; Wir verwenden diese Optionen, um Medien mithilfe der Verwendung der use.path zu beheben.Path Hook mit import.meta.url relativ zur Komponente - Auf dem Server müssen wir das Stammverzeichnis kennen, um dies zu erreichen. Auf der clientseitigen Seite ist es jedoch etwas einfacher, da wir das Root basierend auf dem Pfad der einzelnen Komponenten kennen. Ebenso mit der path , in der wir die Domäne angeben, auf der der Webserver ausgeführt wird; Wir verwenden dies, um den Medien absolute Pfade bereitzustellen, damit Komponenten in Anwendungen von Drittanbietern verwendet werden können. Da es jedoch optional ist, verwenden wir das oben erwähnte root , um einen relativen Pfad anzugeben, der vollkommen in Ordnung ist, wenn wir unsere Komponenten nur auf unserem eigenen Webserver verwenden.
Verwenden der Verwendung use.env Hook Wir können auf diese definierten Parameter sowie einige zusätzliche Elemente zugreifen.
import { create , use } from "switzerland" ;
export default create ( "x-countries" , ( ) => {
const { path , root , node , isServer , isClient } = use . env ( ) ;
return (
< >
{ node && < h1 > Hey { node . nodeName } ! </ h1 > }
< p > Server: { isServer } </ p >
< p > Client: { isClient } </ p >
< ul >
< li > Japan </ li >
< li > Croatia </ li >
< li > Singapore </ li >
</ ul >
</ >
) ;
} ) ; Sie können auch native HTML-Elemente mit der Taste-Syntax x-hello:button in der Funktion create erweitern. Es wird ein x-hello -benutzerdefiniertes Element erstellt, das sich über den button Konstruktor erstreckt, sodass Sie es ihm hinzufügen können.
import { create , use } from "switzerland" ;
export default create ( "x-hello:button" , ( ) => {
const handleClick = use . callback ( ( ) : void => console . log ( "Hello!" ) , [ ] ) ;
return < button onClick = { handleClick } > Say Hello! </ button > ;
} ) ;