Astro กับ Svelte: ใช้ Svelte Components ใน Astro

#astro13 เม.ย. 2569

Astro กับ Svelte: ใช้ Svelte Components ใน Astro

Astro เป็น framework ที่ออกแบบมาให้ทำงานร่วมกับ UI framework อื่นได้อย่างยืดหยุ่น หนึ่งในตัวเลือกที่น่าสนใจคือ Svelte ซึ่งเป็น framework ที่มีขนาดเล็ก ทำงานเร็ว และมี syntax ที่เรียบง่าย บทความนี้จะพาคุณเรียนรู้วิธีการใช้ Svelte Components ภายใน Astro Project

ทำไมต้องใช้ Svelte กับ Astro?

Svelte มีจุดเด่นหลายอย่างที่ทำให้มันเข้ากันได้ดีกับ Astro:

  • Compiled Output: Svelte compile component เป็น vanilla JavaScript ทำให้ bundle size เล็กมาก
  • No Virtual DOM: Svelte ไม่ใช้ Virtual DOM ทำให้ performance ดีกว่า React หรือ Vue ในหลายกรณี
  • Reactive Declarations: การจัดการ state ใน Svelte เรียบง่ายและอ่านง่ายมาก
  • Built-in Animations: Svelte มี transition และ animation API ในตัว

การติดตั้ง Svelte Integration

เริ่มต้นด้วยการติดตั้ง Svelte integration สำหรับ Astro:

npx astro add svelte

คำสั่งนี้จะติดตั้ง @astrojs/svelte และ svelte package พร้อมกับอัปเดต astro.config.mjs ให้อัตโนมัติ

หรือจะติดตั้งเองแบบ manual:

npm install @astrojs/svelte svelte

จากนั้นแก้ไข astro.config.mjs:

import { defineConfig } from 'astro/config';
import svelte from '@astrojs/svelte';

export default defineConfig({
  integrations: [svelte()],
});

สร้าง Svelte Component แรก

สร้างไฟล์ src/components/Counter.svelte:

<script lang="ts">
  let count: number = 0;

  function increment() {
    count += 1;
  }

  function decrement() {
    count -= 1;
  }

  function reset() {
    count = 0;
  }
</script>

<div class="counter">
  <h2>Counter: {count}</h2>
  <div class="buttons">
    <button on:click={decrement}>-</button>
    <button on:click={reset}>Reset</button>
    <button on:click={increment}>+</button>
  </div>
</div>

<style>
  .counter {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
    padding: 2rem;
    border: 1px solid #ccc;
    border-radius: 8px;
  }

  .buttons {
    display: flex;
    gap: 0.5rem;
  }

  button {
    padding: 0.5rem 1rem;
    font-size: 1rem;
    cursor: pointer;
    border-radius: 4px;
    border: 1px solid #333;
    background: #f0f0f0;
  }

  button:hover {
    background: #e0e0e0;
  }
</style>

ใช้ Svelte Component ใน Astro Page

ใน Astro page ให้ import และใช้ Svelte component:

---
import Counter from '../components/Counter.svelte';
---

<html lang="th">
  <head>
    <title>Astro + Svelte Demo</title>
  </head>
  <body>
    <h1>ทดสอบ Svelte Component</h1>
    
    <!-- Static render (no interactivity) -->
    <Counter />
    
    <!-- Client-side hydration -->
    <Counter client:load />
    
    <!-- Hydrate when visible -->
    <Counter client:visible />
  </body>
</html>

Client Directives ที่ควรรู้

Astro มี client directives หลายแบบสำหรับควบคุมการ hydrate:

Directive คำอธิบาย
client:load Hydrate ทันทีที่ page โหลด
client:idle Hydrate เมื่อ browser ว่าง
client:visible Hydrate เมื่อ component เข้ามาใน viewport
client:media Hydrate เมื่อ media query match
client:only Render บน client เท่านั้น

การส่ง Props ไปยัง Svelte Component

สร้าง component ที่รับ props:

<script lang="ts">
  export let name: string;
  export let initialCount: number = 0;
  
  let count = initialCount;
</script>

<div>
  <p>สวัสดี, {name}!</p>
  <p>Count: {count}</p>
  <button on:click={() => count++}>เพิ่ม</button>
</div>

ใช้งานใน Astro:

---
import Greeting from '../components/Greeting.svelte';
---

<Greeting name="นักพัฒนา" initialCount={5} client:load />

Svelte Stores กับ Astro

สำหรับ shared state ระหว่าง components ใช้ Svelte stores:

// src/stores/cartStore.ts
import { writable, derived } from 'svelte/store';

interface CartItem {
  id: string;
  name: string;
  price: number;
  quantity: number;
}

export const cartItems = writable<CartItem[]>([]);

export const cartTotal = derived(cartItems, ($items) =>
  $items.reduce((total, item) => total + item.price * item.quantity, 0)
);

export function addToCart(item: CartItem) {
  cartItems.update((items) => {
    const existing = items.find((i) => i.id === item.id);
    if (existing) {
      return items.map((i) =>
        i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
      );
    }
    return [...items, item];
  });
}

ใช้ store ใน Svelte component:

<script lang="ts">
  import { cartItems, cartTotal, addToCart } from '../stores/cartStore';
</script>

<div>
  <p>จำนวนสินค้า: {$cartItems.length}</p>
  <p>ราคารวม: {$cartTotal} บาท</p>
</div>

Svelte Transitions ใน Astro

หนึ่งในจุดเด่นของ Svelte คือ built-in transitions:

<script lang="ts">
  import { fade, fly, slide } from 'svelte/transition';
  
  let visible = false;
</script>

<button on:click={() => visible = !visible}>
  Toggle
</button>

{#if visible}
  <div transition:fade={{ duration: 300 }}>
    <p>Fade in/out</p>
  </div>
  
  <div in:fly={{ y: 50, duration: 400 }} out:slide>
    <p>Fly in, Slide out</p>
  </div>
{/if}

สรุป

การใช้ Svelte กับ Astro เป็นการผสมผสานที่ลงตัวมาก Astro จัดการ static content และ routing ในขณะที่ Svelte ดูแล interactive components ด้วย bundle size ที่เล็กและ performance ที่ดีเยี่ยม คู่นี้เหมาะสำหรับการสร้าง website ที่ต้องการทั้งความเร็วและ interactivity