สวิตเซอร์แลนด์ใช้วิธีการใช้งานได้กับส่วนประกอบของเว็บโดยใช้ preact กับ Shadow Dom สำหรับการห่อหุ้มสไตล์องค์ประกอบที่กำหนดเองสำหรับการทำงานร่วมกันและการแสดงผลฝั่งเซิร์ฟเวอร์เพื่อความเป็นสากล
เส้นด้าย : yarn add switzerland
NPM : npm install switzerland
cdn : https://cdn.jsdelivr.net/npm/switzerland@latest/dist/index.client.js
สวิตเซอร์แลนด์เป็นทางเลือกเริ่มต้นด้วยการเรนเดอร์ฝั่งเซิร์ฟเวอร์ด้วยความชุ่มชื้นบนไคลเอนต์ด้วย Shadow 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 มาตรฐาน จากนั้นเราสามารถก้าวไปอีกขั้นและอนุญาตให้ประเทศของเราถูกส่งผ่านเป็นแอตทริบิวต์ HTML บนโหนด DOM โดยใช้ <x-countries list="Japan,Croatia,Singapore">
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 ) ;
} ) ;ในฐานะที่เป็นส่วนประกอบของเราเป็นโมดูลที่มีอยู่ในตัวเองการเปลี่ยนแปลงใด ๆ ในแอตทริบิวต์ของพวกเขาจะเริ่มต้นการแสดงผลของต้นไม้ของส่วนประกอบอีกครั้งโดยไม่คำนึงว่าคุณลักษณะเหล่านั้นจะเปลี่ยนจากภายในส่วนประกอบอื่นหรือผ่านทางวานิลลา DOM
const node = document . querySelector ( "x-countries" ) ;
node . attributes . values = ` ${ node . attributes . values } ,Ukraine,Maldives` ; สวิตเซอร์แลนด์ไม่ จำเป็น ต้องรวบรวมยกเว้น typescript เสริมและ JSX transpiling เนื่องจากใช้โมดูล ES ดั้งเดิมในเบราว์เซอร์และ Node 16+ บนเซิร์ฟเวอร์ มันประสบความสำเร็จโดยใช้ node_modules เมื่อแสดงผลบนเซิร์ฟเวอร์โดยใช้การนำเข้าที่มีชื่อและในเบราว์เซอร์จะใช้แผนที่นำเข้าเพื่อแก้ไขการนำเข้าที่มีชื่อไปยัง URL CDN ซึ่งให้การแคชขั้นสูง เราให้บริการยูทิลิตี้สำหรับเซิร์ฟเวอร์เพื่อสร้างแผนที่นำเข้าโดยอัตโนมัติสำหรับแอปพลิเคชันของคุณตามการพึ่งพา
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 เพื่อแก้ไขการพึ่งพา URL jspm.io โดยค่าเริ่มต้น - อย่างไรก็ตามคุณอาจผ่านตัวเลือก provider เพื่อเปลี่ยนผู้ให้บริการ
เมื่อคุณได้รับการกำหนดค่าแผนที่นำเข้าเมื่อการแสดงผลส่วนประกอบของสวิตเซอร์แลนด์ในเบราว์เซอร์จะใช้ URL CDN เหล่านั้นและป้องกันไม่ให้มีความจำเป็นในการจัดเก็บการพึ่งพาการพึ่งพาผ่าน Bundler คุณสามารถมุ่งเน้นไปที่งานง่าย ๆ ของ transpiling typepiling และ JSX ในโมดูล ES ดั้งเดิมโดยใช้อะไรมากไปกว่า tsc - แม้ว่าคุณต้องการลดน้อยลงคุณอาจต้องเพิ่ม 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 ปกติที่กำหนดไว้กับต้นไม้ส่วนประกอบของเรา เราสามารถแนบสไตล์ชีทของเราเข้ากับส่วนประกอบของเราโดยใช้โหนด link ปกติแม้ว่าสวิตเซอร์แลนด์จะให้ยูทิลิตี้ node สำหรับ StyleSheet และ Variables - หลังใช้ตัวแปรที่กำหนดเองกับแผนผังส่วนประกอบของคุณทำให้ 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 ง่าย ๆ และก็ใช้ได้เช่นกัน การใช้ Hook 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 >
) ;
} ) ; เราให้ ID ที่ไม่ซ้ำกันกับฟังก์ชั่น loader ซึ่ง ควร ระบุคำขอแต่ละรายการเพื่อป้องกันการทำซ้ำและเพื่อให้สามารถกระทบยอดกับลูกค้าได้ ด้วยอาร์กิวเมนต์การพึ่งพาในตำแหน่งที่สามเราสามารถเรียกใช้ด้านไคลเอนต์ตัว 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 ) ;
} ) ; เราใช้ตัวเลือกเหล่านี้เพื่อแก้ไขสื่อโดยใช้ use.path hook ด้วย import.meta.url เทียบกับส่วนประกอบ - บนเซิร์ฟเวอร์เราจำเป็นต้องรู้ไดเร็กทอรีรูทเพื่อให้ได้สิ่งนี้ ในฝั่งไคลเอ็นต์ แต่มันง่ายกว่าเล็กน้อยเนื่องจากเรารู้ว่ารูทขึ้นอยู่กับเส้นทางของแต่ละองค์ประกอบ ในทำนองเดียวกันกับตัวเลือก path ที่เราระบุโดเมนที่เว็บเซิร์ฟเวอร์กำลังทำงานอยู่ เราใช้สิ่งนี้เพื่อให้เส้นทางที่แน่นอนไปยังสื่อเพื่อให้สามารถใช้ส่วนประกอบในแอปพลิเคชันของบุคคลที่สามอย่างไรก็ตามเนื่องจากเป็นทางเลือกเราจึงใช้ root ดังกล่าวเพื่อระบุเส้นทางสัมพัทธ์ซึ่งดีอย่างสมบูรณ์เมื่อเราใช้ส่วนประกอบของเราบนเว็บเซิร์ฟเวอร์ของเราเองเท่านั้น
การใช้ hook 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 >
</ >
) ;
} ) ; นอกจากนี้คุณยังสามารถขยายองค์ประกอบ HTML ดั้งเดิมโดยใช้ไวยากรณ์ x-hello:button ในฟังก์ชั่น create -มันจะสร้างองค์ประกอบที่กำหนดเอง x-hello ที่ขยายจากตัวสร้าง 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 > ;
} ) ;