บล็อกนี้เราจะมาทำความรู้จักไลบรารี่ใหม่ที่คล้ายๆ กับ React ที่ใช้ในการพัฒนาเว็บแอปพลิเคชัน ชื่อของไลบรารี่นี้ก็คือ SolidJS
SolidJS ไลบรารี่นี้เอาไว้ใช้สร้างเว็บแอปพลิเคชันแบบ Reactive มีการสร้าง Component ที่สามารถนำกลับมาใช้งานใหม่ได้ และรองรับการสร้าง Component ด้วย JSX เหมือนกับ React เลย (ถ้าเรามีพื้นฐาน React ที่ดีแล้ว ปรับจูนเรื่องโครงสร้างภาษาอีกนิดหน่อยก็สามารถใช้งาน SolidJS ได้อย่างง่ายดายแล้ว)
เหตุผลที่เลือกใช้ SolidJS
- SolidJS เป็นไลบรารี่ใหม่ มีความโดดเด่นเรื่องประสิทธิภาพและความเร็วในการประมวลผล และมีความคล้ายกับ React เป็นอย่างมาก
- SolidJS ปัจจุบันเป็นเวอร์ 1.1 อาจยังไม่เหมาะสมกับการนำไปใช้ใน Production หรือ โปรเจคสเกลใหญ่ เนื่องจาก ไลบรารี่เสริม และชุมชนนักพัฒนาที่จะช่วยแก้ไขปัญหายังมีไม่เยอะ ดังนั้น SolidJS จึงเหมาะสมแก่การศึกษาไว้ล่วงหน้า มากกว่านำไปใช้ใน Production
- SolidJS รองรับทั้ง JavaScript และ TypeScript และมี Playground ให้ฝึกบนเว็บไซต์แบบออนไลน์ได้ด้วย (SolidJS Playground)
ความเหมือนและความแตกต่างระหว่าง React และ SolidJS
- การรองรับ การสร้าง Component ด้วย JSX เหมือนกัน
โค้ดของ React และ SolidJS ทั้งสองไลบรารี่ รองรับการแสดงผล Component ด้วย JSX เหมือนกัน จะสังเกตุว่า JSX จะแตกต่างจาก HTML ทั่วไปคือ ทุก tag จะต้องมี tag เปิด และ ปิด เสมอ เช่น
<div>Hello World!</div>
ส่วน HTML บางตัว ไม่จำเป็นต้องมี tag ปิดก็ได้ เช่น <input type=”text” name=”label”> tag แบบนี้จะใช้ไม่ได้ใน JSX
เรามาดูตัวอย่างโค้ดของ React และ SolidJS ในการสร้าง Component ง่ายๆ กัน
import ReactDOM from "react-dom";
export default function App() {
return <div>Hello World!</div>;
}
ReactDOM.render(<App />,document.getElementById("root"));
import { render } from 'solid-js/web';
function HelloWorld() {
return <div>Hello World!</div>;
}
render(() => <HelloWorld />, document.getElementById('app'))
- รองรับการสร้าง Component แบบ Nested Component ซ้อนๆ กัน ได้เหมือนกัน
ตัวอย่างที่ 2 ทดสอบการรองรับการแสดงผล Component ที่ซ้อนกัน โดย ทั้ง React และ SolidJS นั้นสามารถแสดงผล Component ที่ซ้อนกันได้ และการแสดงผล Component ในแต่ละตัวนั้นจะต้องอยู่บน Root Fragment เท่านั้น (จะมี Root Component เพียง 1 อันเท่านั้น อย่างเช่นในตัวอย่างจะใช้ <> … </>)
import ReactDOM from "react-dom";
const NestedComp = () => <p>This is a Paragraph</p>;
export default function App() {
return (
<>
<div>Hello World!</div>
<NestedComp />
</>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
import { render } from "solid-js/web";
const NestedComp = ()=> <p>This is a Paragraph</p>
function App() {
return (
<>
<h1>This is a Header</h1>
<NestedComp />
</>
);
}
render(() => <App />, document.getElementById("app"));
- รองรับการใช้ hook function และ function component เหมือนกัน
จากตัวอย่างในข้อ 1 และ 2 จะเห็นว่าทั้ง React และ SolidJS นั้นรองรับการเขียนโปรแกรมแบบ function และ สามารถสร้าง Component ที่เป็นแบบ Function Component ได้ด้วย
ส่วนการรองรับ hook function ทั้ง React และ SolidJS ก็รองรับเช่นกัน ตัวอย่างจะแสดงในข้อถัดไป เช่นการใช้ useState, createSignal, useEffect, createEffect เป็นต้น
- การเก็บค่าตัวแปร state ใน React ใช้ useState , SolidJS ใช้ createSignal
import ReactDOM from "react-dom";
import { useEffect, useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
setInterval(() => setCount((c) => c + 1), 1000);
}, []);
return <div>Count: {count}</div>;
}
ReactDOM.render(<Counter />, document.getElementById("root"));
import { render } from "solid-js/web";
import { createSignal } from "solid-js";
function Counter() {
const [count, setCount] = createSignal(0);
setInterval(() => setCount(count() + 1), 1000);
return <div>Count: {count()}</div>;
}
render(() => <Counter />, document.getElementById('app'));
- การเรียกตัวแปรใน useState จะเรียกเป็น ตัวแปร/ฟังก์ชัน และ createSignal จะเรียกแบบเป็นฟังก์ชัน getter/setter
// ประกาศตัวแปร Signal ชื่อว่า count เป็นฟังก์ชันที่เรียกใช้เพื่อดูค่าข้อมูล และ setCount เป็นฟังก์ชันที่เรียกใช้เพื่อแก้ไขข้อมูล
const [count, setCount] = createSignal(0);
// ตัวอย่างการเรียกใช้ count จะต้องเรียกแบบฟังก์ชัน คือ count()
return <div>Count: {count()}</div>;
// ตัวอย่างการเรียกใช้ setCount จะต้องเรียกแบบฟังก์ชัน คือ setCount(param)
setCount(count() + 1)
- การใช้แอฟแฟค React ใช้ useEffect , SolidJS ใช้ createEffect
import { render } from 'solid-js/web';
import { createSignal, createEffect } from 'solid-js';
function Counter() {
const [count, setCount] = createSignal(0);
createEffect(() => {
console.log("The count is now", count());
});
return <button onClick={() => setCount(count() + 1)}>Click Me</button>;
}
render(() => <Counter />, document.getElementById('app'));
- การจดจำค่าไม่ให้กลับไปคำนวนซ้ำเดิม React ใช้ useMemo , SolidJS ใช้ createMemo
import { render } from 'solid-js/web';
import { createSignal, createMemo } from 'solid-js';
function fibonacci(num) {
if (num <= 1) return 1;
return fibonacci(num - 1) + fibonacci(num - 2);
}
function Counter() {
const [count, setCount] = createSignal(10);
const fib = createMemo(() => fibonacci(count()));
return (
<>
<button onClick={() => setCount(count() + 1)}>Count: {count()}</button>
<div>1. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
<div>2. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
<div>3. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
<div>4. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
<div>5. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
<div>6. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
<div>7. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
<div>8. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
<div>9. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
<div>10. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
</>
);
}
render(() => <Counter />, document.getElementById('app'))
- If ใน React จะใช้วิธีการ { show && <Comp />}
ส่วน SolidJS ใช้ Show (Show เป็น Component ในไลบรารี่ solid-js)
import { render } from 'solid-js/web';
import { createSignal, Show } from 'solid-js';
function App() {
const [loggedIn, setLoggedIn] = createSignal(false);
const toggle = () => setLoggedIn(!loggedIn())
return (
<Show
when={loggedIn()}
fallback={() => <button onClick={toggle}>Log in</button>}
>
<button onClick={toggle}>Log out</button>
</Show>
);
}
render(() => <App />, document.getElementById('app'))
- For Loop ใน React ใช้วิธีการไล่สมาชิกในอาเรย์ด้วยฟังก์ชัน map เช่น { list && list.map(i=><Comp />) }
ส่วน SolideJS ใช้ For และ Index(For และ Index เป็น Component ในไลบรารี่ solid-js)
import { render } from 'solid-js/web';
import { createSignal, For } from 'solid-js';
function App() {
const [cats, setCats] = createSignal([
{ id: 'J---aiyznGQ', name: 'Keyboard Cat' },
{ id: 'z_AbfPXTKms', name: 'Maru' },
{ id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
]);
return (
<ul>
<For each={cats()}>{(cat, i) =>
<li>
<a target="_blank" href={`https://www.youtube.com/watch?v=${cat.id}`}>
{i() + 1}: {cat.name}
</a>
</li>
}</For>
</ul>
);
}
render(() => <App />, document.getElementById('app'))
import { render } from 'solid-js/web';
import { createSignal, Index } from 'solid-js';
function App() {
const [cats, setCats] = createSignal([
{ id: 'J---aiyznGQ', name: 'Keyboard Cat' },
{ id: 'z_AbfPXTKms', name: 'Maru' },
{ id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
]);
return (
<ul>
<Index each={cats()}>{(cat, i) =>
<li>
<a target="_blank" href={`https://www.youtube.com/watch?v=${cat().id}`}>
{i + 1}: {cat().name}
</a>
</li>
}</Index>
</ul>
);
}
render(() => <App />, document.getElementById('app'))
การใช้งาน For และ Index ที่เป็น component ในไลบรารี่ของ Solid นั้น จะมีข้อแตกต่างในการใช้งาน ดังนี้
- For จะส่งค่า object และ ค่า index ที่เป็น signal ให้ ดังนั้น เวลาที่เรานำค่า index มาใช้จะต้องมี () ด้านหลังตัวแปรเสมอ
- Index จะส่งค่า object ที่เป็น signal และ ค่า index ที่เป็นค่า fix ให้ ดังนั้น เวลาที่เรานำค่า object มาใช้ จะต้องมี () ด้านหลังตัวแปรเสมอ
- เมื่อต้องการเช็คเงื่อนไขที่มีหลายทางเลือก ใน JavaScript จะใช้ switch/case
ส่วน SolidJS จะเตรียม Switch และ Match มาให้ใน ไลบรารี่ solid-js แล้ว เราสามารถเขียนโค้ดเงื่อนไขลงใน JSX ได้เลย
import { render } from "solid-js/web";
import { createSignal, Switch, Match } from "solid-js";
function App() {
const [x] = createSignal(7);
return (
<Switch fallback={<p>{x()} is between 5 and 10</p>}>
<Match when={x() > 10} >
<p>{x()} is greater than 10</p>
</Match>
<Match when={5 > x()}>
<p>{x()} is less than 5</p>
</Match>
</Switch>
);
}
render(() => <App />, document.getElementById("app"));
- Context ทั้ง React และ SolidJS จะใช้วิธีเดียวกันและชื่อฟังก์ชันที่เรียกใช้ก็เหมือนกันคือ createContext และ useContext ใช้วิธีการครอบ Component ด้วย Context Provider เหมือนกัน
import { createSignal, createContext, useContext } from "solid-js";
const CounterContext = createContext();
export function CounterProvider(props) {
const [count, setCount] = createSignal(props.count || 0),
store = [
count,
{
increment() {
setCount(c => c + 1);
},
decrement() {
setCount(c => c - 1);
}
}
];
return (
<CounterContext.Provider value={store}>
{props.children}
</CounterContext.Provider>
);
}
export function useCounter() { return useContext(CounterContext); }
การนำ CounterProvider ไปใช้งานที่ component ที่ต้องการส่งผ่านค่า count และ ใช้งานฟังก์ชัน setCount
import { render } from "solid-js/web";
import Nested from "./nested";
import { CounterProvider } from "./counter";
function App() {
return <>
<h1>Welcome to Counter App</h1>
<Nested />
</>
};
render(() => (
<CounterProvider count={1}>
<App />
</CounterProvider>
), document.getElementById("app"));
import { useCounter } from "./counter";
export default function Nested() {
const [count, { increment, decrement }] = useCounter();
return (
<>
<div>{count()}</div>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</>
);
};
สรุป
SolidJS เป็นไลบรารี่ที่ช่วยอำนวยความสะดวกในการสร้างเว็บแอปพลิเคชัน แบบ Reactive ได้ โดยจะมีโครงการการเขียนโปรแกรมคล้ายกับ ReactJS หากเรามีพื้นฐานการเขียน React อยู่แล้วก็จะสามารถเรียนรู้ SolidJS ได้เร็วยิ่งขึ้น
ข้อดีของ SolidJS คือเรื่องของประสิทธิภาพการทำงาน สามารถรันผลลัพธ์ได้เร็วเทียบเท่ากับการรันของ JavaScript ปกติ (ช้ากว่า JavaScript ปกติเพียง 1.05 เท่า) และ รองรับภาษา TypeScript
ตัวอย่างไลบรารี่ที่น่าสนใจของ SolidJS ดังนี้
- SolidJS สามารถเขียนโค้ด FLow Control ในโค้ดของ JSX ได้ (ในไลบรารี่ solid-js)
- สามารถเขียน style และ class กำกับใน component เพื่อกำหนดรูปแบบการแสดงผลแบบ CSS ได้เลย
- สามารถเขียนโปรแกรมให้รองรับ Server Side Rendering (SSR) (ในไลบรารี่ solid-ssr)
- มี store เอาไว้เก็บค่าตัวแปรเพื่อนำไปใช้ยัง component อื่นๆ ได้ง่าย (ในไลบรารี่ solid-js/store)
- มีฟังก์ชันที่ช่วยในเรื่องการโหลด component และ ข้อมูล อย่าง lazy และ createResource (ในไลบรารี่ solid-js)
SolidJS ก็เป็นอีกหนึ่งไลบรารี่ที่น่าสนใจ และในไลบรารี่ที่ SolidJS เตรียมไว้ให้นั้นยังมีเรื่องที่น่าสนใจอยู่อีกมากมาย สามารถศึกษาต่อได้ตามลิ้งอ้างอิงได้เลยครับ