スイスは、スタイルのカプセル化のためにシャドウDOMを使用して事前にアクセスし、相互運用性のためのカスタム要素、普遍性のためのサーバー側のレンダリングを使用して、Webコンポーネントに機能的なアプローチを取ります。
糸: yarn add switzerland
NPM : npm install switzerland
CDN : https://cdn.jsdelivr.net/npm/switzerland@latest/dist/index.client.js
スイスはオプションで、宣言的なシャドウDOMのおかげで、クライアントの水分補給とのサーバー側のレンダリングから始まります。これは、事前に使用されているため、コンポーネントが非常に馴染みがあります。
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関数を使用する必要があります。関数にオプションの2番目のパラメーターを指定できますが、現在、コンポーネントはデータフェッチやメディアインクルージョンを実行していないため、不要です。
import { render } from "switzerland" ;
app . get ( "/" , async ( _ , response ) => {
const html = await render ( < Countries list = "Japan,Croatia,Singapore" /> ) ;
response . send ( html ) ;
} ) ;コンポーネントは自己完結型モジュールであるため、それらの属性が別のコンポーネント内から変更されるか、バニラDOMアクセサーズを介して変更されるかどうかに関係なく、属性の変更はコンポーネントのツリーの再レンダーを開始します。
const node = document . querySelector ( "x-countries" ) ;
node . attributes . values = ` ${ node . attributes . values } ,Ukraine,Maldives` ; スイスは、オプションのTypeScriptとJSXトランスピーリングを除き、コンパイルする必要はありません。これは、ブラウザでネイティブESモジュールとサーバーでノード16+を使用するためです。これは、名前付きインポートを使用してサーバーをレンダリングするときに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を使用し、バンドラーを介して依存関係をパッケージ化する必要性を防ぎます。 tsc以外のものを使用して、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
}
}プアクトを使用してスイスのコンポーネントをレンダリングするため、APIはすでに馴染みがあるはずです。使いやすさのために、Preactのフック機能を再輸出しますが、Preactから直接使用することもできます。
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ドキュメントを使用できます。スイスは、通常のlinkノードを使用してスタイルシートをコンポーネントに接続できますが、スイスはStyleSheetとVariablesにnodeユーティリティを提供します。後者は、CSSがそれらのJavaScript変数にアクセスできるようにするコンポーネントツリーにカスタム変数を適用します。 use.path hookを使用して、コンポーネントに比べてメディア(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でサーバー側のレンダリングを許可しているため、データの取得にはローダーユーティリティフックが提供されます。ただし、他のサードパーティフェッチのユーティリティまたは単純なuseEffectを使用することもできます。 loaderフックを使用すると、データサーバー側を取得し、クライアントの再フェッチを防ぐことができます。これを達成し、以前にカバーした非同期render関数でコンポーネントを2回レンダリングし、ツリーにシリアル化されたデータを含めます。
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を提供します。依存関係の引数が3番目の位置にあるため、パラメーターが変更されるたびにloaderクライアント側を再度侵入できます。私たちの場合、何も変更されていない場合は、おそらく再フェッチしたくないでしょうが、特定のリストでフェッチする場合、現在の国のリストが依存関係として提供されると予想されるかもしれません。
環境コンテキストを提供するには、サーバー側のユーザー構成が必要です。 render関数は、Webサーバーのルートディレクトリとオプションでサーバーを実行しているドメインの両方を指定できるオプションの2番目のパラメーターを使用します。
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 ) ;
} ) ;これらのオプションを使用して、コンポーネントに対してimport.meta.urlを使用してuse.pathを使用してメディアを解決します。これを達成するには、ルートディレクトリを知る必要があります。ただし、クライアント側では、各コンポーネントのパスに基づいてルートを知っているため、少し簡単です。同様に、Webサーバーが実行されているドメインを指定するpathオプションを使用します。これを使用してメディアへの絶対パスを提供して、サードパーティアプリケーションでコンポーネントを利用できるようにしますが、オプションであるため、前述のrootを使用して、独自のWebサーバーでコンポーネントのみを使用する場合に完全に適切な相対パスを指定します。
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要素を拡張することもできます。これはbuttonコンストラクターから拡張されるx-helloカスタム要素を作成し、独自のひねりを加えることができます。
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 > ;
} ) ;