瑞士使用帶有Shadow dom的PREAXCT採用功能性方法來進行Web組件,用於樣式封裝,互操作性的自定義元素以及用於通用性的服務器端渲染。
紗線: yarn add switzerland
NPM : npm install switzerland
CDN : https://cdn.jsdelivr.net/npm/switzerland@latest/dist/index.client.js
瑞士(Switzerland)可選地始於服務器端渲染,從而通過聲明的Shadow dom在客戶端進行水合 - 由於我們對preact的使用,我們的組件看起來非常熟悉。
import { create } from "switzerland" ;
export default create ( "x-countries" , ( ) => {
return (
< ul >
< li > Japan </ li >
< li > Croatia </ li >
< li > Singapore </ li >
</ ul >
) ;
} ) ;一旦定義了x-countries組件,我們就可以將其呈現在服務器上並在客戶端上作為標準<x-countries /> dom元素進行補充。然後,我們可以進一步邁出一步,並允許使用<x-countries list="Japan,Croatia,Singapore">在DOM節點上作為HTML屬性通過。
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 >
) ;
} ) ;在節點環境中使用我們的組件需要我們使用導出的異步render函數;我們可以為函數指定可選的第二個參數,但是我們的組件當前不執行數據獲取或媒體包含,因此不必要。
import { render } from "switzerland" ;
app . get ( "/" , async ( _ , response ) => {
const html = await render ( < Countries list = "Japan,Croatia,Singapore" /> ) ;
response . send ( html ) ;
} ) ;由於我們的組件是獨立的模塊,因此對其屬性的任何更改都會啟動組件的樹的重新渲染 - 無論這些屬性是從另一個組件內還是通過Vanilla dom登錄器變化。
const node = document . querySelector ( "x-countries" ) ;
node . attributes . values = ` ${ node . attributes . values } ,Ukraine,Maldives` ; 瑞士不需要編譯,除了可選的打字稿和JSX轉移,因為它在服務器上使用了瀏覽器和節點16+的本機ES模塊。當使用命名導入在服務器上渲染時,它通過使用node_modules來實現這一目標,並且在瀏覽器中,它使用導入地圖來解決提供增強式緩存的CDN URL的命名導入。我們為服務器提供了一個實用程序,可以根據其依賴項自動為您的應用程序生成導入地圖。
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>
` ) ;
} ) ;您需要將imports功能作為瑞士組件的基本路徑。然後,它將使用ts-morph提供代碼的抽象語法樹(AST),並允許我們挑選外部依賴關係;然後,它迭代地與您選擇的軟件包管理器安裝的版本相匹配。我們使用@jspm/generator軟件包將依賴項解析到jspm.io URL默認情況下 - 但是,您還可以通過provider選項來更改提供商。
配置了導入地圖後,在瀏覽器中渲染瑞士組件時,它將使用這些CDN URL,並阻止通過Bundler包裝依賴項的任何需求。您可以純粹專注於使用tsc將Typescript和JSX轉換為本機ES模塊的簡單任務 - 儘管如果您想縮小,則可能需要添加Terser。
{
"include" : [ " src " ],
"compilerOptions" : {
"rootDir" : " ./src " ,
"outDir" : " ./dist " ,
"module" : " esnext " ,
"moduleResolution" : " nodenext " ,
"esModuleInterop" : true ,
"target" : " esnext " ,
"strict" : true ,
"jsx" : " react-jsx " ,
"jsxImportSource" : " preact " ,
"declaration" : true
}
}由於我們使用preact渲染瑞士的組件,因此API應該已經熟悉了。為了易於使用,我們可以重新脫離掛鉤功能,但您也可以直接從預見中使用它們。
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 >
) ;
} ) ; 陰影邊界內的樣式允許封裝,這意味著我們可以使用範圍範圍內的常規CSS文檔。儘管瑞士為StyleSheet和Variables提供了node實用程序,但我們可以通過使用常規link節點將樣式表附加到組件上,但後者將自定義變量應用於組件樹,允許CSS訪問CSS訪問這些JavaScript變量。我們使用use.path鉤子解決媒體 - 相對於我們的組件,CSS文檔,圖像等。
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" />
</ >
) ;
} ) ;然後,在將這些樣式應用於我們的組件上時,我們可能會變得非常鬆散,知道陰影邊界將阻止樣式洩漏 - 我們使用CSS變量來應用有條件的背景顏色,並帶有後備。
: host {
box - shadow : 0 0 5px # e8c5b0;
}
ul {
background - col or : var( --background-color , "#E39AC7" );
}由於瑞士允許默認情況下使用服務器端渲染,因此提供了用於use.loader數據的Loader Utility Hook - 儘管您可以選擇使用任何其他第三方提取實用程序或簡單的useEffect ,這也很好。使用loader Hook允許獲取數據服務器端,然后防止對客戶端重新提取;我們通過在我們前面介紹的異步render函數中兩次渲染組件,然後在樹上包含串行數據來實現這一目標。
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 >
) ;
} ) ;我們為loader函數提供了獨特的ID,該ID應該標識單個請求以防止重複並允許對客戶端進行對帳。使用第三位置的依賴項參數,我們可以在參數更改時重新啟動loader客戶端。在我們的案例中,我們可能不想重新挑選任何更改,但是如果通過給定列表提取,我們可能會期望當前提供的國家 /地區作為依賴關係。
提供環境上下文需要服務器端上的某些用戶配置 - render函數採用可選的第二個參數,該參數允許我們在Web-Server上指定根目錄,並且可以選擇我們正在運行服務器的域。
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 ) ;
} ) ;我們使用這些選項使用use.path hook與import.meta.url相對於組件來解析媒體 - 在服務器上,我們需要知道根目錄以實現這一目標。但是,在客戶端上,這更簡單,因為我們根據每個組件的路徑知道根。同樣,使用path選項,我們指定了網絡服務器正在運行的域;我們使用它來提供媒體的絕對路徑,以便可以在第三方應用程序中使用組件,但是由於它是可選的,因此我們使用上述root指定相對路徑,當我們僅在自己的網絡服務器上使用組件時,該路徑完全很好。
使用use.env掛鉤我們可以訪問這些定義的參數以及一些其他項目。
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 >
</ >
) ;
} ) ; 您還可以使用x-hello:button create函數中的按鈕語法擴展本機HTML元素 - 它將創建一個x-hello自定義元素,該X自定義元素從button構造函數延伸,從而使您可以為其添加自己的扭曲。
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 > ;
} ) ;