Menu Close

SolidJS : พื้นฐานการสร้างเว็บแอปพลิเคชันด้วย SolidJS

บล็อกนี้เราจะมาทำความรู้จักไลบรารี่ใหม่ที่คล้ายๆ กับ 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

  1. การรองรับ การสร้าง 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'))
  1. รองรับการสร้าง 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"));
  1. รองรับการใช้ hook function และ function component เหมือนกัน

จากตัวอย่างในข้อ 1 และ 2 จะเห็นว่าทั้ง React และ SolidJS นั้นรองรับการเขียนโปรแกรมแบบ function และ สามารถสร้าง Component ที่เป็นแบบ Function Component ได้ด้วย

ส่วนการรองรับ hook function ทั้ง React และ SolidJS ก็รองรับเช่นกัน ตัวอย่างจะแสดงในข้อถัดไป เช่นการใช้ useState, createSignal, useEffect, createEffect เป็นต้น

  1. การเก็บค่าตัวแปร 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'));
  1. การเรียกตัวแปรใน 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)
  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'));
  1. การจดจำค่าไม่ให้กลับไปคำนวนซ้ำเดิม 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'))
  1. 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'))
  1. 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 มาใช้ จะต้องมี () ด้านหลังตัวแปรเสมอ

  1. เมื่อต้องการเช็คเงื่อนไขที่มีหลายทางเลือก ใน 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"));
  1. 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 เตรียมไว้ให้นั้นยังมีเรื่องที่น่าสนใจอยู่อีกมากมาย สามารถศึกษาต่อได้ตามลิ้งอ้างอิงได้เลยครับ

อ้างอิง

  1. https://www.solidjs.com/
  2. https://www.solidjs.com/tutorial/introduction_components

Posted in solidjs, typescript

ใส่ความเห็น