تتخذ سويسرا نهجًا وظيفيًا لمكونات الويب باستخدام PREACT مع Shadow DOM لتغليف النمط ، والعناصر المخصصة للتشغيل البيني وتقديم جانب الخادم للعالمية.
الغزل : yarn add switzerland
NPM : npm install switzerland
CDN : https://cdn.jsdelivr.net/npm/switzerland@latest/dist/index.client.js
تبدأ سويسرا اختياريا مع عرض جانب الخادم مع الترطيب على العميل بفضل DEDOW 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. يمكننا بعد ذلك اتخاذ خطوة إلى الأمام والسماح لبلداننا بتمريرها كسمة 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 لأنها تستخدم وحدات ES الأصلية في المتصفح والعقدة 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 لحل التبعيات إلى jspm.io عناوين URL بشكل افتراضي - ومع ذلك يمكنك أيضًا تمرير خيار provider لتغيير المزود.
بمجرد تكوين خريطة الاستيراد ، عند تقديم مكونات سويسرا في المتصفح ، ستستخدم عناوين URL CDN هذه ومنع أي حاجة إلى زيادة التبعيات عبر Bundler. يمكنك التركيز بحتة على المهمة البسيطة المتمثلة في Transpiling TypeScript و 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 لجعل مكونات سويسرا ، يجب أن تكون واجهة برمجة التطبيقات مألوفة بالفعل. لسهولة الاستخدام ، نقوم بإعادة تصدير وظائف ربط 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 لحل الوسائط - مستندات 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 التي يجب أن تحدد الطلب الفردي لمنع التكرارات والسماح بالمصالحة على العميل. مع وجود وسيطة التبعيات في الموضع الثالث ، يمكننا إعادة صياغة جانب العميل 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 مع 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 >
</ >
) ;
} ) ; يمكنك أيضًا تمديد عناصر 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 > ;
} ) ;