ในโลกของ Frontend Development ปี 2026 การเลือก Framework ที่เหมาะสมเป็นสิ่งสำคัญมาก React และ Vue ครองตลาดมานาน แต่มี Framework ตัวหนึ่งที่กำลังเติบโตอย่างรวดเร็วและได้รับความนิยมจากนักพัฒนาทั่วโลก นั่นคือ Svelte และ Meta-framework ของมันอย่าง SvelteKit ที่ถูกออกแบบมาให้เร็ว เขียนง่าย และมี Bundle Size เล็กที่สุดในบรรดา Framework ชั้นนำทั้งหมด
บทความนี้จะพาคุณเรียนรู้ Svelte ตั้งแต่แนวคิดพื้นฐานที่แตกต่างจาก Framework อื่น ไปจนถึงการสร้าง Full-stack Application ด้วย SvelteKit รวมถึงฟีเจอร์ล่าสุดอย่าง Svelte 5 Runes ที่จะเปลี่ยนวิธีการเขียน Reactive Code ไปตลอดกาล
Svelte คืออะไร? ทำไมจึงแตกต่าง
Svelte คือ Frontend Framework ที่สร้างโดย Rich Harris ในปี 2016 สิ่งที่ทำให้ Svelte แตกต่างจาก React หรือ Vue อย่างสิ้นเชิงคือ Svelte เป็น Compiler ไม่ใช่ Runtime Library
ใน React หรือ Vue เมื่อคุณเขียน Component เสร็จ Framework จะส่ง Runtime Library (Virtual DOM, Reconciler ฯลฯ) ไปกับ Code ของคุณด้วย ทำให้ Bundle Size ใหญ่ขึ้น และ Browser ต้องทำงานหนักขึ้นในการ Diff และ Patch DOM
แต่ Svelte ทำงานคนละแบบ มันจะ Compile Component ของคุณให้เป็น Vanilla JavaScript ตอน Build Time ผลลัพธ์คือ Code ที่ Manipulate DOM โดยตรง ไม่มี Virtual DOM ไม่มี Runtime Overhead ทำให้:
- Bundle Size เล็กกว่ามาก — ไม่ต้องส่ง Framework Runtime ไปกับ App
- Performance ดีกว่า — Update DOM โดยตรง ไม่ผ่าน Virtual DOM Diffing
- เขียน Code น้อยกว่า — Syntax กระชับ ไม่ต้อง Boilerplate มาก
- เรียนรู้ง่ายกว่า — ใกล้เคียงกับ HTML/CSS/JS ปกติ
Svelte vs React vs Vue เปรียบเทียบแบบละเอียด
| คุณสมบัติ | Svelte | React | Vue |
|---|---|---|---|
| ประเภท | Compiler | Runtime Library | Runtime Library |
| Virtual DOM | ไม่มี | มี | มี |
| Bundle Size (min) | ~2 KB | ~42 KB | ~33 KB |
| Reactivity | Compile-time | useState/useEffect | ref/reactive |
| Syntax | ใกล้ HTML | JSX | SFC (.vue) |
| Learning Curve | ง่าย | ปานกลาง | ปานกลาง |
| State Management | Stores (built-in) | Redux/Zustand | Pinia/Vuex |
| Meta-framework | SvelteKit | Next.js/Remix | Nuxt |
| Animations | Built-in | Third-party | Built-in (basic) |
| Community Size | กำลังโต | ใหญ่ที่สุด | ใหญ่ |
เริ่มต้นกับ Svelte — การติดตั้ง
การเริ่มต้นโปรเจกต์ Svelte ใหม่ในปี 2026 แนะนำให้ใช้ SvelteKit เป็นหลัก เพราะเป็น Official Framework ที่รองรับทั้ง SPA, SSR และ Static Site Generation
# สร้างโปรเจกต์ SvelteKit ใหม่
npx sv create my-app
cd my-app
# เลือก template:
# - SvelteKit demo app
# - SvelteKit minimal
# - Svelte library
npm install
npm run dev
# เปิด browser ที่ http://localhost:5173
โครงสร้างโปรเจกต์ SvelteKit:
my-app/
├── src/
│ ├── lib/ # Shared components & utilities
│ │ └── index.js # $lib alias
│ ├── routes/ # File-based routing
│ │ ├── +page.svelte # หน้า Home
│ │ ├── +layout.svelte # Layout wrapper
│ │ └── about/
│ │ └── +page.svelte # /about page
│ ├── app.html # HTML template
│ └── app.css # Global styles
├── static/ # Static assets
├── svelte.config.js # SvelteKit config
├── vite.config.js # Vite config
└── package.json
Svelte Component พื้นฐาน
Svelte Component เขียนในไฟล์ .svelte ซึ่งประกอบด้วย 3 ส่วน: Script, Markup และ Style เหมือนกับ HTML ปกติ
<!-- Counter.svelte -->
<script>
let count = 0;
function increment() {
count += 1;
}
function decrement() {
count -= 1;
}
function reset() {
count = 0;
}
</script>
<div class="counter">
<h2>Counter: {count}</h2>
<button on:click={decrement}>-</button>
<button on:click={reset}>Reset</button>
<button on:click={increment}>+</button>
</div>
<style>
.counter {
text-align: center;
padding: 20px;
}
button {
margin: 0 8px;
padding: 8px 16px;
font-size: 1.2em;
border-radius: 4px;
cursor: pointer;
}
</style>
สังเกตว่า Svelte ไม่ต้องใช้ useState หรือ ref แค่ประกาศตัวแปรธรรมดา แล้ว Svelte Compiler จะทำให้มันเป็น Reactive โดยอัตโนมัติ การ Assign ค่าใหม่ให้ตัวแปร (count += 1) จะ Trigger การ Update UI ทันที
Reactivity ใน Svelte — หัวใจของ Framework
Reactivity คือระบบที่ทำให้ UI อัพเดตตาม Data อัตโนมัติ Svelte มีระบบ Reactivity ที่เรียบง่ายแต่ทรงพลังมาก
Reactive Declarations ($:)
ใช้ $: เพื่อสร้าง Derived Values ที่คำนวณใหม่อัตโนมัติเมื่อ Dependencies เปลี่ยน:
<script>
let width = 10;
let height = 5;
// Reactive declaration — คำนวณใหม่เมื่อ width หรือ height เปลี่ยน
$: area = width * height;
$: perimeter = 2 * (width + height);
// Reactive statement — รันทุกครั้งที่ค่าเปลี่ยน
$: if (area > 100) {
console.log('Area exceeded 100!');
}
$: {
console.log(`Width: ${width}, Height: ${height}`);
console.log(`Area: ${area}, Perimeter: ${perimeter}`);
}
</script>
<input type="range" bind:value={width} min="1" max="20">
<input type="range" bind:value={height} min="1" max="20">
<p>Area: {area} | Perimeter: {perimeter}</p>
arr = [...arr, newItem] การ Push ไม่ Trigger Update เพราะ Reference ไม่เปลี่ยน ใน Svelte 5 ปัญหานี้ถูกแก้ด้วย Runes
Two-way Binding
<script>
let name = '';
let email = '';
let agreed = false;
let color = '#ff0000';
let volume = 50;
let flavor = 'vanilla';
let sizes = [];
</script>
<!-- Text input -->
<input type="text" bind:value={name} placeholder="Name">
<input type="email" bind:value={email} placeholder="Email">
<!-- Checkbox -->
<label>
<input type="checkbox" bind:checked={agreed}>
I agree to terms
</label>
<!-- Range -->
<input type="range" bind:value={volume} min="0" max="100">
<!-- Select -->
<select bind:value={flavor}>
<option value="vanilla">Vanilla</option>
<option value="chocolate">Chocolate</option>
<option value="strawberry">Strawberry</option>
</select>
<!-- Multiple checkbox (group) -->
<label><input type="checkbox" bind:group={sizes} value="S"> S</label>
<label><input type="checkbox" bind:group={sizes} value="M"> M</label>
<label><input type="checkbox" bind:group={sizes} value="L"> L</label>
Components — Props, Events และ Slots
Props (รับข้อมูลจาก Parent)
<!-- Card.svelte -->
<script>
export let title;
export let description = 'No description'; // default value
export let variant = 'default'; // default value
</script>
<div class="card {variant}">
<h3>{title}</h3>
<p>{description}</p>
</div>
<!-- การใช้งาน -->
<Card title="My Card" description="Card content" variant="primary" />
<Card title="Simple Card" />
Events (ส่ง Event ไป Parent)
<!-- SearchBar.svelte -->
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
let query = '';
function handleSearch() {
dispatch('search', { query });
}
function handleClear() {
query = '';
dispatch('clear');
}
</script>
<div class="search-bar">
<input bind:value={query} on:keydown={(e) => e.key === 'Enter' && handleSearch()}>
<button on:click={handleSearch}>Search</button>
<button on:click={handleClear}>Clear</button>
</div>
<!-- Parent ใช้งาน -->
<SearchBar on:search={(e) => console.log(e.detail.query)} on:clear={() => console.log('cleared')} />
Slots (Content Projection)
<!-- Modal.svelte -->
<script>
export let show = false;
export let title = 'Modal';
</script>
{#if show}
<div class="modal-backdrop" on:click={() => show = false}>
<div class="modal" on:click|stopPropagation>
<header>
<slot name="header">
<h2>{title}</h2>
</slot>
</header>
<main>
<slot>
<p>Default content</p>
</slot>
</main>
<footer>
<slot name="footer">
<button on:click={() => show = false}>Close</button>
</slot>
</footer>
</div>
</div>
{/if}
<!-- การใช้งาน -->
<Modal bind:show={showModal}>
<h2 slot="header">Custom Title</h2>
<p>This is the modal body content</p>
<div slot="footer">
<button on:click={() => showModal = false}>Cancel</button>
<button on:click={save}>Save</button>
</div>
</Modal>
Svelte Stores — State Management ในตัว
Svelte มีระบบ Store ในตัวสำหรับจัดการ State ที่แชร์ข้าม Components โดยไม่ต้องติดตั้ง Library เพิ่ม
Writable Store
// stores.js
import { writable } from 'svelte/store';
// สร้าง Store
export const count = writable(0);
export const user = writable(null);
export const theme = writable('light');
// Custom store with methods
function createTodoStore() {
const { subscribe, set, update } = writable([]);
return {
subscribe,
add: (text) => update(todos => [...todos, {
id: Date.now(),
text,
done: false
}]),
toggle: (id) => update(todos =>
todos.map(t => t.id === id ? { ...t, done: !t.done } : t)
),
remove: (id) => update(todos =>
todos.filter(t => t.id !== id)
),
clear: () => set([])
};
}
export const todos = createTodoStore();
ใช้ Store ใน Component
<script>
import { count, todos } from './stores.js';
// Auto-subscription ด้วย $ prefix
// ไม่ต้อง subscribe/unsubscribe เอง!
</script>
<p>Count: {$count}</p>
<button on:click={() => $count += 1}>+1</button>
<button on:click={() => count.set(0)}>Reset</button>
<h3>Todos ({$todos.length})</h3>
{#each $todos as todo (todo.id)}
<div>
<input type="checkbox" checked={todo.done} on:change={() => todos.toggle(todo.id)}>
<span class:done={todo.done}>{todo.text}</span>
<button on:click={() => todos.remove(todo.id)}>X</button>
</div>
{/each}
Readable และ Derived Stores
import { readable, derived } from 'svelte/store';
// Readable store — ค่าที่เปลี่ยนจากภายนอก
export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => set(new Date()), 1000);
return function stop() { clearInterval(interval); };
});
// Derived store — คำนวณจาก Store อื่น
export const elapsed = derived(time, ($time) =>
Math.round(($time - startTime) / 1000)
);
// Derived จากหลาย Stores
export const fullName = derived(
[firstName, lastName],
([$firstName, $lastName]) => `${$firstName} ${$lastName}`
);
// Derived จาก todos — นับเฉพาะที่ยังไม่เสร็จ
export const pendingCount = derived(todos, ($todos) =>
$todos.filter(t => !t.done).length
);
Lifecycle ของ Svelte Component
<script>
import { onMount, onDestroy, beforeUpdate, afterUpdate, tick } from 'svelte';
// onMount — หลัง Component ถูก Render ครั้งแรก
onMount(() => {
console.log('Component mounted');
// เหมาะสำหรับ fetch data, setup event listeners
const data = await fetch('/api/data').then(r => r.json());
// Return cleanup function (optional)
return () => {
console.log('Cleanup on destroy');
};
});
// onDestroy — ก่อน Component ถูกลบ
onDestroy(() => {
console.log('Component destroyed');
});
// beforeUpdate — ก่อน DOM update
beforeUpdate(() => {
console.log('About to update DOM');
});
// afterUpdate — หลัง DOM update
afterUpdate(() => {
console.log('DOM updated');
});
// tick() — รอจนกว่า DOM จะ update เสร็จ
async function handleClick() {
count += 1;
await tick(); // รอ DOM update
// ตอนนี้ DOM อัพเดตแล้ว
}
</script>
Transitions และ Animations
Svelte มีระบบ Transition และ Animation ในตัวที่ทรงพลังมาก ไม่ต้องติดตั้ง Library เพิ่ม:
<script>
import { fade, fly, slide, scale, blur, draw } from 'svelte/transition';
import { flip } from 'svelte/animate';
import { quintOut } from 'svelte/easing';
let visible = true;
let items = [1, 2, 3, 4, 5];
</script>
<!-- Basic transition -->
<button on:click={() => visible = !visible}>Toggle</button>
{#if visible}
<div transition:fade>Fades in and out</div>
<div transition:fly={{ y: 200, duration: 500 }}>Flies in from below</div>
<div transition:slide>Slides in</div>
<div transition:scale={{ start: 0.5 }}>Scales in</div>
<div transition:blur={{ amount: 10 }}>Blurs in</div>
{/if}
<!-- in/out แยกกัน -->
{#if visible}
<div in:fly={{ x: -200 }} out:fade>
Flies in from left, fades out
</div>
{/if}
<!-- Animate list items -->
{#each items as item (item)}
<div animate:flip={{ duration: 300 }}>
Item {item}
</div>
{/each}
SvelteKit — Full-stack Framework
SvelteKit คือ Official Meta-framework ของ Svelte (เทียบได้กับ Next.js สำหรับ React หรือ Nuxt สำหรับ Vue) มีฟีเจอร์ครบครันสำหรับสร้าง Full-stack Web Application
File-based Routing
SvelteKit ใช้ระบบ File-based Routing ที่โครงสร้างโฟลเดอร์ใน src/routes กำหนด URL:
src/routes/
├── +page.svelte # / (หน้าแรก)
├── +layout.svelte # Layout สำหรับทุกหน้า
├── +error.svelte # Error page
├── about/
│ └── +page.svelte # /about
├── blog/
│ ├── +page.svelte # /blog
│ ├── +page.server.js # Server load function
│ └── [slug]/
│ ├── +page.svelte # /blog/:slug (dynamic route)
│ └── +page.server.js # Load data for each post
├── api/
│ └── users/
│ └── +server.js # API endpoint: GET/POST /api/users
└── (auth)/ # Route group (ไม่ส่งผลต่อ URL)
├── login/
│ └── +page.svelte # /login
└── register/
└── +page.svelte # /register
+page.svelte — หน้าเว็บ
<!-- src/routes/blog/+page.svelte -->
<script>
export let data; // ข้อมูลจาก load function
</script>
<svelte:head>
<title>Blog Posts</title>
<meta name="description" content="Our latest blog posts">
</svelte:head>
<h1>Blog</h1>
{#each data.posts as post}
<article>
<h2><a href="/blog/{post.slug}">{post.title}</a></h2>
<p>{post.excerpt}</p>
<time>{post.date}</time>
</article>
{/each}
+page.server.js — Server Load Function
// src/routes/blog/+page.server.js
import { error } from '@sveltejs/kit';
export async function load({ fetch, params, url }) {
const page = url.searchParams.get('page') || 1;
const response = await fetch(`/api/posts?page=${page}`);
if (!response.ok) {
throw error(404, 'Posts not found');
}
const posts = await response.json();
return {
posts,
page: Number(page),
totalPages: posts.totalPages
};
}
// src/routes/blog/[slug]/+page.server.js
export async function load({ params }) {
const { slug } = params;
const post = await getPostBySlug(slug);
if (!post) {
throw error(404, 'Post not found');
}
return { post };
}
+layout.svelte — Layout ที่ครอบทุกหน้า
<!-- src/routes/+layout.svelte -->
<script>
import '../app.css';
export let data;
</script>
<nav>
<a href="/">Home</a>
<a href="/blog">Blog</a>
<a href="/about">About</a>
{#if data.user}
<span>{data.user.name}</span>
{:else}
<a href="/login">Login</a>
{/if}
</nav>
<main>
<slot />
</main>
<footer>
<p>Copyright 2026</p>
</footer>
+server.js — API Routes
// src/routes/api/users/+server.js
import { json, error } from '@sveltejs/kit';
export async function GET({ url }) {
const page = url.searchParams.get('page') || 1;
const users = await db.getUsers(page);
return json(users);
}
export async function POST({ request }) {
const body = await request.json();
if (!body.name || !body.email) {
throw error(400, 'Name and email are required');
}
const user = await db.createUser(body);
return json(user, { status: 201 });
}
export async function PUT({ request }) {
const body = await request.json();
const user = await db.updateUser(body.id, body);
return json(user);
}
export async function DELETE({ url }) {
const id = url.searchParams.get('id');
await db.deleteUser(id);
return new Response(null, { status: 204 });
}
Form Actions — จัดการ Form แบบ Progressive Enhancement
// src/routes/login/+page.server.js
import { fail, redirect } from '@sveltejs/kit';
export const actions = {
login: async ({ request, cookies }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
if (!email || !password) {
return fail(400, { email, message: 'All fields are required' });
}
const user = await authenticateUser(email, password);
if (!user) {
return fail(401, { email, message: 'Invalid credentials' });
}
cookies.set('session', user.token, {
path: '/',
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 60 * 60 * 24 * 7 // 7 days
});
throw redirect(303, '/dashboard');
},
register: async ({ request }) => {
// Handle registration
}
};
<!-- src/routes/login/+page.svelte -->
<script>
import { enhance } from '$app/forms';
export let form; // ข้อมูลจาก fail()
</script>
<form method="POST" action="?/login" use:enhance>
{#if form?.message}
<p class="error">{form.message}</p>
{/if}
<input name="email" type="email" value={form?.email ?? ''} required>
<input name="password" type="password" required>
<button type="submit">Login</button>
</form>
use:enhance เพื่อเพิ่ม UX ดีขึ้นเมื่อมี JS
Server-Side Rendering (SSR) ใน SvelteKit
SvelteKit รองรับ Rendering หลายแบบในแต่ละหน้า:
// src/routes/+page.js — ทั้ง Server และ Client
export const prerender = true; // Static Site Generation
export const ssr = true; // Server-Side Render (default)
export const csr = true; // Client-Side Render (default)
// ตัวเลือก Rendering:
// 1. SSR (default) — Render บน Server ทุก Request
// 2. Prerender — Render ตอน Build (Static HTML)
// 3. CSR only — Render บน Client เท่านั้น
// 4. SSR + CSR — Render Server ก่อน แล้ว Hydrate บน Client
// ปิด SSR สำหรับ SPA-only page
export const ssr = false;
// Prerender เฉพาะบางหน้า
export const prerender = true;
SvelteKit Adapters — Deploy ทุกที่
SvelteKit ใช้ระบบ Adapter เพื่อ Build ให้เหมาะกับ Platform ต่างๆ:
// svelte.config.js
import adapter from '@sveltejs/adapter-auto'; // เลือกให้อัตโนมัติ
export default {
kit: {
adapter: adapter()
}
};
// Adapter ที่มีให้เลือก:
// @sveltejs/adapter-auto — ตรวจจับ Platform อัตโนมัติ
// @sveltejs/adapter-node — Node.js Server
// @sveltejs/adapter-static — Static Site (HTML/CSS/JS)
// @sveltejs/adapter-vercel — Vercel
// @sveltejs/adapter-cloudflare — Cloudflare Pages
// @sveltejs/adapter-netlify — Netlify
# ติดตั้ง Adapter
npm i -D @sveltejs/adapter-node
# Build
npm run build
# Preview
npm run preview
# Deploy (ตัวอย่าง Node.js)
node build/index.js
SvelteKit vs Next.js เปรียบเทียบ
| คุณสมบัติ | SvelteKit | Next.js |
|---|---|---|
| Base Framework | Svelte (Compiler) | React (Runtime) |
| Bundle Size | เล็กกว่ามาก | ใหญ่กว่า (React Runtime) |
| Routing | File-based (+page.svelte) | File-based (page.tsx) |
| SSR/SSG | ทั้งคู่ | ทั้งคู่ |
| API Routes | +server.js | route.ts |
| Form Handling | Form Actions (built-in) | Server Actions |
| State Management | Stores (built-in) | ต้องเลือก Library |
| Animations | Built-in transitions | ต้องใช้ Framer Motion |
| Learning Curve | ง่ายกว่า | ซับซ้อนกว่า |
| Community | เล็กกว่าแต่โตเร็ว | ใหญ่มาก |
| Job Market | น้อยกว่า | มากกว่า |
Svelte 5 Runes — อนาคตของ Svelte
Svelte 5 เปิดตัว Runes ระบบ Reactivity ใหม่ที่ทำให้ Svelte ทรงพลังและยืดหยุ่นมากขึ้น เป็นการเปลี่ยนแปลงครั้งใหญ่ที่สุดของ Svelte:
$state — แทนที่ let
<script>
// Svelte 4 (เดิม)
let count = 0;
// Svelte 5 (Runes)
let count = $state(0);
let user = $state({ name: 'John', age: 30 });
let items = $state([1, 2, 3]);
// Deep reactivity — ใน Svelte 5 push() ทำงานได้แล้ว!
function addItem() {
items.push(items.length + 1); // Reactive ใน Svelte 5
}
</script>
$derived — แทนที่ $:
<script>
let width = $state(10);
let height = $state(5);
// Svelte 4
// $: area = width * height;
// Svelte 5
let area = $derived(width * height);
let perimeter = $derived(2 * (width + height));
let isLarge = $derived(area > 100);
// Complex derived
let summary = $derived.by(() => {
if (area > 100) return 'Large';
if (area > 50) return 'Medium';
return 'Small';
});
</script>
$effect — แทนที่ $: statements
<script>
let count = $state(0);
// Svelte 5 effect — รันเมื่อ dependencies เปลี่ยน
$effect(() => {
console.log(`Count changed to ${count}`);
document.title = `Count: ${count}`;
// Cleanup function
return () => {
console.log('Cleaning up previous effect');
};
});
// $effect.pre — รันก่อน DOM update
$effect.pre(() => {
console.log('Before DOM update');
});
</script>
$props — แทนที่ export let
<script>
// Svelte 4
// export let title;
// export let description = '';
// Svelte 5
let { title, description = '', ...rest } = $props();
</script>
<div {...rest}>
<h2>{title}</h2>
<p>{description}</p>
</div>
Component Libraries สำหรับ Svelte
ในปี 2026 มี Component Library ที่ดีหลายตัวสำหรับ Svelte:
Skeleton UI
# ติดตั้ง
npx sv create my-app
# เลือก Skeleton ใน setup wizard
# หรือเพิ่มเข้าโปรเจกต์เดิม
npm i -D @skeletonlabs/skeleton @skeletonlabs/tw-plugin
shadcn-svelte
# ติดตั้ง
npx shadcn-svelte init
# เพิ่ม Components
npx shadcn-svelte add button
npx shadcn-svelte add card
npx shadcn-svelte add dialog
# ใช้งาน
<script>
import { Button } from '$lib/components/ui/button';
import * as Card from '$lib/components/ui/card';
</script>
<Button variant="outline">Click me</Button>
<Card.Root>
<Card.Header>
<Card.Title>Title</Card.Title>
</Card.Header>
<Card.Content>Content here</Card.Content>
</Card.Root>
Flowbite Svelte
npm i -D flowbite-svelte flowbite
# มี Components มากกว่า 50 ตัว:
# Alert, Badge, Button, Card, Dropdown, Modal, Navbar,
# Pagination, Table, Tabs, Toast, Tooltip, etc.
Testing Svelte Applications
Unit Testing ด้วย Vitest
# ติดตั้ง
npm i -D vitest @testing-library/svelte @testing-library/jest-dom jsdom
// vite.config.js
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
plugins: [svelte({ hot: !process.env.VITEST })],
test: {
include: ['src/**/*.test.js'],
environment: 'jsdom',
globals: true
}
});
// src/lib/Counter.test.js
import { render, fireEvent } from '@testing-library/svelte';
import { expect, test } from 'vitest';
import Counter from './Counter.svelte';
test('renders initial count', () => {
const { getByText } = render(Counter);
expect(getByText('Counter: 0')).toBeTruthy();
});
test('increments count on click', async () => {
const { getByText } = render(Counter);
const button = getByText('+');
await fireEvent.click(button);
expect(getByText('Counter: 1')).toBeTruthy();
});
E2E Testing ด้วย Playwright
# ติดตั้ง
npm i -D @playwright/test
// playwright.config.js
import { defineConfig } from '@playwright/test';
export default defineConfig({
webServer: {
command: 'npm run build && npm run preview',
port: 4173
},
testDir: 'tests'
});
// tests/home.test.js
import { test, expect } from '@playwright/test';
test('homepage has correct title', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveTitle(/My App/);
});
test('navigation works', async ({ page }) => {
await page.goto('/');
await page.click('a[href="/about"]');
await expect(page.locator('h1')).toHaveText('About');
});
test('form submission works', async ({ page }) => {
await page.goto('/login');
await page.fill('input[name="email"]', 'test@test.com');
await page.fill('input[name="password"]', 'password');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
});
Performance Benchmarks
จากการทดสอบ JavaScript Framework Benchmark ปี 2026:
| Metric | Svelte 5 | React 19 | Vue 3.5 | Solid |
|---|---|---|---|---|
| Create 1000 rows | 42ms | 68ms | 55ms | 39ms |
| Update every 10th | 18ms | 32ms | 24ms | 16ms |
| Swap rows | 21ms | 45ms | 28ms | 19ms |
| Remove row | 15ms | 28ms | 20ms | 14ms |
| Startup time | 12ms | 35ms | 25ms | 11ms |
| Memory (MB) | 2.1 | 4.8 | 3.2 | 2.0 |
| Bundle (gzip) | 1.8 KB | 42 KB | 33 KB | 7 KB |
เมื่อไหร่ควรเลือก Svelte
Svelte เหมาะกับโปรเจกต์ประเภทเหล่านี้:
- Performance-critical Apps — แอปที่ต้องโหลดเร็ว Bundle เล็ก เช่น E-commerce, Landing Pages
- Progressive Web Apps — ต้องการ App ที่เร็วบน Mobile
- ทีมขนาดเล็กถึงกลาง — Svelte เหมาะกับทีมที่ต้องการ Productivity สูง เขียน Code น้อยแต่ได้ผลมาก
- Static Sites & Blogs — SvelteKit + adapter-static สร้าง Static Site ที่เร็วมาก
- Prototype & MVP — เขียน Code น้อย สร้างได้เร็ว
- เริ่มต้นเรียน Frontend — Syntax ใกล้ HTML/CSS/JS เรียนรู้ง่าย
Svelte อาจไม่เหมาะถ้า:
- ต้องการ Job Market ที่ใหญ่ (React ยังนำ)
- ต้องการ Ecosystem ที่สมบูรณ์ที่สุด (React มี Library มากกว่า)
- ทีมใหญ่ที่ใช้ React/Vue อยู่แล้ว (Migration Cost สูง)
ตัวอย่างโปรเจกต์จริง — SvelteKit Blog
// svelte.config.js
import adapter from '@sveltejs/adapter-auto';
import { mdsvex } from 'mdsvex';
export default {
extensions: ['.svelte', '.md'],
preprocess: [mdsvex({ extensions: ['.md'] })],
kit: {
adapter: adapter()
}
};
// src/routes/blog/[slug]/+page.server.js
export async function load({ params }) {
try {
const post = await import(`../../../content/${params.slug}.md`);
return {
content: post.default,
meta: post.metadata
};
} catch (e) {
throw error(404, 'Post not found');
}
}
// src/routes/blog/[slug]/+page.svelte
<script>
export let data;
</script>
<svelte:head>
<title>{data.meta.title}</title>
</svelte:head>
<article>
<h1>{data.meta.title}</h1>
<time>{data.meta.date}</time>
<svelte:component this={data.content} />
</article>
สรุป
Svelte และ SvelteKit เป็น Framework ที่เปลี่ยนวิธีคิดเกี่ยวกับ Frontend Development โดยพื้นฐาน แนวคิด Compiler-first ทำให้ได้ Bundle Size เล็ก Performance สูง และ Developer Experience ที่ยอดเยี่ยม Svelte 5 Runes ยกระดับ Reactivity ให้ทรงพลังยิ่งขึ้นพร้อมรักษาความเรียบง่ายที่เป็นเอกลักษณ์ของ Svelte
ในปี 2026 SvelteKit เป็นตัวเลือกที่ดีเยี่ยมสำหรับทั้ง Side Project และ Production Application ถ้าคุณเบื่อกับความซับซ้อนของ React หรือต้องการ Framework ที่เร็วและเขียนง่าย Svelte คือคำตอบที่ควรลอง เริ่มต้นวันนี้ด้วย npx sv create my-app แล้วคุณจะเข้าใจว่าทำไม Developer ทั่วโลกถึงหลงรัก Svelte
