스위스는 스타일 캡슐화를위한 Shadow Dom과 함께 Preact, 상호 운용성을위한 사용자 정의 요소 및 보편성을위한 서버 측 렌더링을 사용하여 웹 구성 요소에 대한 기능적 접근 방식을 취합니다.
원사 : yarn add switzerland
NPM : npm install switzerland
cdn : https://cdn.jsdelivr.net/npm/switzerland@latest/dist/index.client.js
스위스는 선택적으로 선언적 섀도우 돔 덕분에 클라이언트의 수화로 서버 측 렌더링으로 시작합니다.
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 ) ;
} ) ;우리의 구성 요소가 자체 포함 된 모듈이므로 속성에 대한 모든 변경 사항은 다른 구성 요소 내부 또는 바닐라 돔 액세서를 통해 이러한 속성이 변경되는지 여부에 관계없이 구성 요소 트리의 재 렌즈를 시작합니다.
const node = document . querySelector ( "x-countries" ) ;
node . attributes . values = ` ${ node . attributes . values } ,Ukraine,Maldives` ; 스위스는 브라우저에서 기본 ES 모듈을 사용하고 서버의 노드 16+를 사용하기 때문에 옵션 TypeScript 및 JSX Transping을 제외하고는 컴파일 할 필요가 없습니다. 이름 지정된 가져 오기를 사용하여 서버에서 렌더링 할 때 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 기능에 스위스 구성 요소의 기본 경로를 제공해야합니다. 그런 다음 코드의 추상 구문 트리 (AST)를 제공하는 ts-morph 사용하여 파일을 가로 지르며 외부 종속성을 선택할 수 있습니다. 그런 다음 선택한 패키지 관리자가 설치 한 버전과 반복적으로 찾은 종속성과 일치합니다. 당사는 @jspm/generator 패키지를 사용하여 기본적으로 jspm.io URL에 대한 종속성을 해결하지만 provider 옵션을 전달하여 공급자를 변경할 수도 있습니다.
일단 가져 오기 맵을 구성하면 브라우저에서 스위스 구성 요소를 렌더링 할 때 해당 CDN URL을 사용하고 번들러를 통해 의존성을 포장 할 필요가 없습니다. 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는 이미 친숙해야합니다. 사용 편의성을 위해 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 문서를 사용할 수 있습니다. 스위스는 StyleSheet 및 Variables 에 대한 node 유틸리티를 제공하지만, 후자는 CSS가 해당 JavaScript 변수에 액세스 할 수 있도록 구성 요소 트리에 사용자 정의 변수를 적용 할 수 있지만 일반 link 노드를 사용하여 구성 요소에 스타일 시트를 연결할 수 있습니다. 우리는 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 함수에서 구성 요소를 두 번 렌더링 한 다음 트리에 직렬화 된 데이터를 포함하여이를 달성합니다.
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를 제공하여 중복을 방지하고 클라이언트의 조정을 허용하기 위해 개별 요청을 식별 해야합니다 . 의존성 인수는 세 번째 위치에 있으면 매개 변수가 변경 될 때마다 loader 클라이언트 측을 다시 침입 할 수 있습니다. 우리의 경우 우리는 아무것도 변경하지 않았을 것입니다. 아무것도 변경되지 않지만 주어진 목록으로 가져 오면 현재 국가 목록이 종속성으로 제공 될 것으로 예상 할 수 있습니다.
환경 컨텍스트를 제공하려면 서버 측에서 일부 사용자 구성이 필요합니다. render 함수는 선택적인 두 번째 매개 변수를 가져와 웹 서버의 루트 디렉토리와 선택적으로 서버를 실행하는 도메인을 지정할 수 있습니다.
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 사용하여 미디어를 해결하여 구성 요소에 비해 서버에서 루트 디렉토리를 알아야합니다. 그러나 클라이언트 측에서는 각 구성 요소의 경로를 기반으로 루트를 알고 있기 때문에 약간 더 간단합니다. 마찬가지로 웹-서버가 실행중인 도메인을 지정하는 path 옵션과 마찬가지로; 우리는 이것을 사용하여 미디어에 대한 절대 경로를 제공하여 구성 요소를 타사 응용 프로그램에서 활용할 수 있지만, 선택 사항이기 때문에 앞서 언급 한 root 사용하여 자신의 웹 서버에 구성 요소 만 사용할 때 완벽하게 괜찮은 상대 경로를 지정합니다.
use.env 사용하여 env hook이 정의 된 매개 변수와 몇 가지 추가 항목에 액세스 할 수 있습니다.
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 x-hello x-hello:button create button 사용하여 기본 HTML 요소를 확장 할 수 있습니다.
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 > ;
} ) ;