ทำเว็บ todo list แบบง่ายๆ โดยใช้ Svelte และ Tailwind CSS เป็นครั้งแรก
และแล้วก็ถึงเวลา... ทำบล็อกเกี่ยวกับ Svelte ซะที หลังจากที่ชาวคณะเริ่มทำกันเว็บที่ใช้ Svelte กันไปนานแล้วหล่ะ
บล็อกนี้เรามาลองทำเว็บไซต์ todo list โดยใช้ Svelte และ Tailwind CSS ซึ่งอ้างอิงจากพี่นาตร จากใน session ของงาน Barcamp Bangkok 2022
เราจะค่อยๆไปพร้อมๆกันเนอะ
Svelte 101
อ้างอิงจาก session พี่หนุ่มที่พูดในงาน Barcamp Bangkok 2022 นะ ซึ่งคลิปย้อนหลังไม่รู้จะมาเมื่อไหร่
Svelte (อ่านว่า สะ-เว่ล-ทึ) เป็น compiler คือ มีการทำ code compile เพื่อให้คอมเข้าใจง่ายขึ้น (เหมือนฝั่ง blockchain ที่มันไม่เข้าใจภาษา solidity เลยต้อง compile ให้มันอ่านรู้เรื่องอ่ะ ประมาณนี้เลย)
Svelte จะเขียนโค้ดที่อัปเดต DOM เมื่อสถานะของแอปเปลี่ยนแปลง ซึ่งเพิ่มมาบูมตอนเป็น Svelte 3 นะ ด้วย Rethinking reactivity
หลักการของ Svelte ไปดูคลิปนี้ได้เลยยย
document ของ Svelte สามารถเข้าไปอ่านได้ที่
และเข้าไปส่อง quickstart guide เพื่อเริ่มทำได้เลย โดยการ run command เพื่อ clone มันออกมาแล้ว run ได้เลย หรือใช้ vite เพื่อทำ hot reload ให้ ก็ได้เช่นกัน
ข้อดีของ Svelte คือทำให้ developer ออกของได้เร็วขึ้น พี่หนุ่มเลยใช้เวลาทำ thwordle 2 วัน แล้วก็อันนี้ ชิง Keychron ใช้เวลาทำชั่วโมงเดียวเสร็จ
Svelte vs Svelte Kit
ทำความรู้จักกับ Svelte กันไปแล้ว มาทำความรู้จัก Svelte Kit กันต่อเลย
เจ้า Svelte Kit เป็น framework ในการสร้างแอพพลิเคชั่น แล้วก็รองรับเรื่อง Routing ต่างๆด้วยนะ
ถ้าเปรียบเทียบง่ายๆ Svelte จะเหมือน React ส่วน Svelte Kit จะเหมือน Next.js นั่นเอง
เรียนรู้ Svelte โดยการทำ todo list กันเถอะ
เราจะค่อยๆไปกันเนอะ
เตรียมตัวกันก่อน
อันนี้ไม่มีอะไรมาก ก็ทำการ install ของต่างๆลงไป
npm install
โปรเจกต์นี้เราจะใช้ Svelte Kit พร้อมลง Tailwind CSS ตามนี้เลย
อันนี้การลง Tailwind CSS สำหรับ Svelte เฉยๆ
โค้ดต่างๆสามารถไปส่องในนี้เพื่อ adapt ไปตามที่เราต้องการได้ เย้ๆ
คิดว่าหลายๆคนคงจะใช้ VS Code กัน แนะนำให้ติด plug-in ของ Svelte ก่อนจ้า
ไฟล์ที่เราไปทำกัน ถ้าเป็น Svelte เฉยๆ จะอยู่ใน src/App.svelte
และถ้าเป็น Svelte Native เอ้ยยย Svelte Kit ก็จะอยู่ใน src/routes/index.svelte
และถ้าเราจะทำการ run เพื่อ ดูหน้าเว็บ สามารถใช้คำสั่งนี้ได้เลยจ้า
npm run dev
ของ Tailwind CSS มี plug-in ลง VS Code ตามนี้เลย
สร้าง text hello world
เรามารู้จักโครงสร้างในไฟล์ Svelte กันก่อนเลย ถ้าใครเคยเขียน Vue.js มาก่อนก็จะคุ้นๆเนอะ ที่ไฟล์นึงมี html, css, และ javascript ซึ่งเจ้า Svelte ก็เหมือนกันเลยหล่ะนะ
มาดูโค้ดนี้กัน ส่วนบนจะเป็นส่วนของ <script>
ประกอบด้วยโค้ด javascript ที่เราเขียนขึ้นมาเพื่อทำงานอะไรบางอย่างก่อนแสดงผล และข้างล่าง ใช่ค่ะ HTML ปกติเลย
<script>
export let name = "world";
</script>
<h1>Hello {name}!</h1>
การทำงาน คือ เอาค่า name
ในที่นี้จะเท่ากับ world เนอะ มาแสดงในหน้าเว็บ ผลที่ได้ก็จะเป็น Hello world! นั่นเอง
ตัวอย่างในเว็บ Svelte
เพิ่ม text input
เป็นการเปลี่ยน text ตาม input ที่เราใส่เข้าไป
ในส่วนของ name
อันนี้เป็นค่าว่างไว้ และค่าจะเปลี่ยนเมื่อมีการเปลี่ยนค่าใน input (ที่ทาง Android เรียกว่า EditText อ่ะ) จะมีการ bind ค่าไปที่ name
และใส่ placeholder ไว้ถ้ามันเป็นค่าว่าง
<script>
export let name = '';
</script>
<input bind:value={name} placeholder="enter something">
<h1>{name}</h1>
หน้าตาจะประมาณนี้ ขี้เกียจทำรูป gif หล่ะนะ
กดปุ่มเพื่อนำค่าจาก input มาอัพเดต
ในตอนนี้เราจะเพิ่มปุ่ม เพื่อทำการอัพเดตข้อมูล ผ่าน event onClick
ด้วย function ที่ชื่อว่า handleClick()
โดยนำค่า input
จะมาจาก input field มาใส่ในค่า output
นั่นเอง
<script>
export let input;
export let output = "";
function handleClick() {
output = input;
}
</script>
<input bind:value={input} placeholder="enter something">
<button on:click={handleClick}>
Add
</button>
<h1>{output}</h1>
ผลที่ได้ก็จะประมาณนี้แหละเนอะ ค่าจะไม่ update แบบ real-time เหมือนมะกี้แล้วนะ
เพิ่ม checkbox เพื่อทำ todo list
ก่อนอื่นเรามาสร้าง array list ตัวนึง เพื่อเก็บ todo list ของเรา ชื่อว่า todoList
ก็แล้วกันเนอะ
สมมุติว่าเรามีของอยู่แล้วใน list เราจะแสดงผลยังไงหล่ะ? เราจะต้องใช้ loop นั่นเอง
โดยใน Svelte จะใช้ syntax ประมาณนี้
<script>
let todoList = ["todo 1", "todo 2", "todo 3"];
</script>
{#each todoList as todo}
<p>{todo}</p>
{/each}
ปกติเราจะเห็น for item to list กันเนอะ แต่ใน Svelte มันจะสลับกันเนอะ เป็น each list as item
แล้วก็ตัวบล็อก มันจะขึ้นต้นด้วย # แล้วจบด้วย / นะ
ผลที่ได้จ้า
จากนั้นมาเพิ่ม checkbox กันหน่อยจ้า โดยมันจะอยู่หน้า text เนอะ เราจะใช้ div ครอบ แล้วเติม input ที่เป็น type checkbox เข้าไป
<script>
let todoList = ["todo 1", "todo 2", "todo 3"];
</script>
{#each todoList as todo}
<div>
<input type=checkbox>
<p>{todo}</p>
</div>
{/each}
พบว่า มันอยู่คนละบรรทัด มันไม่กลางอ่า
แน่นอนว่าการจัดให้มันอยู่แถวเดียวกันนั้น เป็นหน้าที่ของ Tailwind CSS นั่นเอง
เราจะวางเจ้า checkbox และ text เป็น flex เนอะ วางในแถวเดียวกัน
{#each todoList as todo}
<div class="flex flex-row">
<input type=checkbox>
<p>{todo}</p>
</div>
{/each}
อ่ะยังไม่สวย งั้นขยับให้เขาห่างกันนิดนึง ให้ text ห่างไปอีกสักนิดนึง
{#each todoList as todo}
<div class="flex flex-row">
<input type=checkbox>
<p class="mx-4">{todo}</p>
</div>
{/each}
เอ่ออออ ทำไมมันตกๆ
งั้นลอง check ดูดีกว่า เหมือน text สูงกว่า checkbox อ่ะ
งั้นขยับ checkbox ให้อยู่ตรงกลางหน่อย
{#each todoList as todo}
<div class="flex flex-row">
<input class="place-self-center" type=checkbox>
<p class="mx-4">{todo}</p>
</div>
{/each}
อ่ะ สวยงามตามท้องเรื่องหล่ะ
เมื่อเราแสดงของใน todoList
ที่เรา mock มาเรียบร้อยแล้ว ทำการลบไส้ในทิ้ง เหลือแค่ array เปล่า
let todoList = [];
เพราะเราจะทำการอัพเดตค่าที่ได้จาก input เข้าไปใส่ใน todoList
นั่นเอง
เราจะสร้าง function ใหม่ ชื่อว่า addTodo
เปลี่ยนจากของเดิมที่เป็น handleClick
เนอะ
การทำงานของ addTodo
ก็ตรงไปตรงมา เอา input ที่ได้จาก input field มาใส่ใน list แล้วเคลียร์ค่า input ที่ได้ออกไป
ในทางปกติ เราจะเพิ่มของใน array list ด้วยท่าประมาณว่า list.add(item) อะไรประมาณนี้ หรือถ้าเขียนเป็น javascript จะเป็น todoList.push(input)
แต่ใน Svelte ไม่สามารถทำท่านี้ได้นะ มันจะไม่ render ให้
ดังนั้นจะใช้ท่า x =...
เพื่อให้เจ้า Svelte รู้ว่า เฮ้ยย ค่านี้เกิดการเปลี่ยนแปลงอ่ะ เป็นการ spread ตัวเองออกมา แล้วเพิ่มของใหม่เข้าไป แล้ว reassign ค่าอีกหนึ่งรอบ
หน้าตาจะเป็นแบบนี้
todoList = [...todoList, input];
หน้าตาโค้ดทั้งหมดในตอนนี้
<script>
export let input = "";
let todoList = [];
function addTodo() {
todoList = [...todoList, input];
console.log(todoList);
input = '';
}
</script>
<h1>TODO LIST</h1>
<input bind:value={input} placeholder="enter something">
<button on:click={addTodo}>
Add
</button>
{#each todoList as todo}
<div>
<input type=checkbox>
<p>{todo}</p>
</div>
{/each}
ผลที่ได้จ้า
กด checkbox สักหน่อย
ถือว่าใช้งานได้หล่ะ จบบล็อกเพียงเท่านี้ เดี๋ยวสิๆๆ กด checkbox แล้วต้องขีดฆ่าทิ้งด้วยสิ ว่าเราทำอันนี้ไปแล้วนะ
กด checkbox เพื่อขีดฆ่า
เมื่อเรากด checkbox ที่ item แล้ว ให้ทำการขีดฆ่า item นั้น
ดังนั้นเราจะปรับอะไรนิดหน่อย จากเดิมที่เป็น array list ของ text เฉยๆ จะเป็น array list ของ object ตัวนึง ชื่อว่าอะไรดีน้า ชื่อ item
ก็แล้วกัน ข้างในจะประกอบด้วย
done
เป็น boolean ไว้ check ว่า อันนี้ถูกติ๊กแล้วหรือยังtext
เป็น text ธรรมดาเลย ว่า todo อันนี้ คืออะไร
จากเดิมที่เราจะอัพ todoList
เมื่อมี input เข้ามา จะเป็นการเอา object ตัวนี้ไปใส่เข้าไปแทน แบบนี้เลย
function addTodo() {
let item = {
done: false,
text: input,
};
todoList = [...todoList, item];
console.log(todoList);
input = '';
}
แน่นอนว่าเราสร้าง object item
มาแล้ว เราจะต้องนำมาใช้งาน
- checkbox จะทำการ bind ค่า จาก
todo.done
- text จะทำการ bind ค่า จาก
todo.text
เมื่อเซฟมากดแล้วยังไม่ได้ถูกขีดฆ่าเหมือนเดิม เพราะ ไม่ได้ handle ขีดฆ่า
เราจะทำการ check ว่า ถ้า todo.done
เป็น true ให้ขีดฆ่า text ถ้าไม่ก็แสดงปกติไปเลย
ซึ่งเราจะต้องใช้ if-else เราอธิบายรวดเลยแล้วกันเนอะ จากโค้ดตัวอย่างนี้
{#if x > 10}
<p>{x} is greater than 10</p>
{:else if 5 > x}
<p>{x} is less than 5</p>
{:else}
<p>{x} is between 5 and 10</p>
{/if}
เปิด if ด้วย # จบด้วย / ส่วน else และ else if ใส่ : นะ
แต่ในเคสนี้ใช้แค่ if-else ปกติเลย ก็จะมีแค่ #if, :else และ /if เนอะ แบบนี้ เราจะขีดฆ่าโดยใช้ <strike>
เนอะ
{#each todoList as todo}
<div class="flex flex-row">
<input class="place-self-center" type=checkbox bind:checked={todo.done}>
<p class="mx-4">
{#if todo.done}
<strike>{todo.text}</strike>
{:else}
{todo.text}
{/if}
</p>
</div>
{/each}
ผลที่ได้จ้า
สวยงาม ถูกต้องจ้า เย้ๆ
โค้ดทั้งหมดจ้าทุกคน
<script>
export let input = "";
let todoList = [];
function addTodo() {
let item = {
done: false,
text: input,
};
todoList = [...todoList, item];
console.log(todoList);
input = '';
}
</script>
<h1>TODO LIST</h1>
<input bind:value={input} placeholder="enter something">
<button on:click={addTodo}>
Add
</button>
{#each todoList as todo}
<div class="flex flex-row">
<input class="place-self-center" type=checkbox bind:checked={todo.done}>
<p class="mx-4">
{#if todo.done}
<strike>{todo.text}</strike>
{:else}
{todo.text}
{/if}
</p>
</div>
{/each}
ลอกมาจาก
ปรับให้สวยงามอีกนิดด้วย Tailwind CSS
มาดูจอทั้งหมดกัน
ชิดซ้ายไปเลยค้าบพี่
เราก็เลยขยายๆมันหน่อย เริ่มจากให้มันขยับออกจากมุมซ้ายบนหน่อย เพิ่มกรอบของ input และ button เพิ่มขนาด text หน่อย ก็น่าจะได้มั้งนะ
โค้ดทั้งหมดของเราในบล็อกนี้
ทิ้งท้าย
เราทำ template ของ Svelte เอาไว้แหละ ใช้ Svelte Kit + Tailwind CSS เอาไว้ใช้ในการขึ้นโปรเจกต่อๆไป
จริงๆคือคนทำกันเยอะมากเลยนะเจ้า template อ่ะ ชอบอันไหนก็เอาอันนั้นไปใช้ได้เลย
ก็หวังว่าจะได้ความรู้กันไปเนอะ เอาจริงๆบล็อกนี้เขียนนานมาก แถมยังเอามาสตรีมอีก อ่ะดูย้อนหลังได้ที่นี่ เพราะ Twitch มันเก็บแค่ 14 วันอ่ะดิ
สามารถ support ค่ากาแฟเจ้าของบล็อกได้ที่ปุ่มแดงส้มสุดน่ารักที่มุมซ้ายล่าง หรือกดปุ่มตรงนี้ก็ได้จ้า
ติดตามข่าวสารและบทความใหม่ๆได้ที่
ช่องทางใหม่ ติดตามทุกๆสตรีมของเราได้ที่
Subscribe ช่อง YouTube ของเราได้ที่
download แอพอ่านบล็อกใหม่ของเราได้ที่นี่