Astro คืออะไร? มาลองทำเว็บด้วย Astro กันครับ

Astro: The web framework for content-driven websites นี่คือสโลแกนของ Astro ในหน้าเว็บ
Astro คือ คือเฟรมเวิร์คสำหรับสร้างเว็บไซต์ที่เน้นเนื้อหา (Content) เป็นหลัก และเน้นเรื่องประสิทธิภาพและความเร็ว โดยใช้แนวคิด “Islands Architecture” ที่ช่วยให้เว็บไซต์โหลดเร็วและมีประสิทธิภาพสูง

เว็บไซต์ของ Astro
ข้อดีของ Astro
- Islands : Frontend architecture ที่ช่วยให้หน้าเว็บโหลดเร็วขึ้น โดยแบ่ง HTML เป็นส่วนเล็กๆ
- Zero JS, by default : เว็บไซต์โหลดเร็วเพราะส่ง JavaScript น้อยที่สุดไปยังผู้ใช้
- รองรับการใช้งานร่วมกับ UI Framework ต่างๆ ทั้ง React, Vue, Svelte
- รองรับทั้ง Static site และ Server Side Rendering (SSR)
- รองรับการจัดการ content ด้วย Markdown/MDX
- มีเรื่อง Content Layer / Collection ที่ช่วยให้จัดการเนื้อหาได้ง่ายขึ้น
- มี built-in
ViewTransition
มาให้ในตัว

คุณสามารถเข้าดุเว็บ astro.new เพื่อดูตัวอย่างได้
เริ่มต้น Astro
มาลองเริ่มต้นสร้างโปรเจ็คด้วย Astro กัน สิ่งที่ต้องมีคือ Node.js (แนะนำ v.18 หรือ v20 ขึ้นไป) ตัว v19 ไม่รองรับ
เวอร์ชั่น Astro คือ v5.0.3 ณ วันที่เขียนบทความ
สร้างโปรเจ็ค ด้วย template minimal
# กรณีใช้ npmnpm create astro@latest -- --template minimal
# กรณีใช้ pnpmpnpm create astro@latest --template minimal
# กรณีใช้ yarnyarn create astro --template minimal
# กรณีใช้ bunbun create astro --template minimal
จากนั้น ตัว prompt ก็จะถามเราว่าจะใช้สร้างโปรเจ็คไว้ที่ folder อะไร รวมถึง ติดตั้ง dependencies และ git repository เลยหรือไม่
เมื่อติดตั้งเสร็จเรียบร้อย จะได้ผลลัพธ์แบบด้านล่าง
astro Launch sequence initiated.
dir Where should we create your new project? ./astro-minimal ◼ tmpl Using minimal as project template
deps Install dependencies? Yes
git Initialize a new git repository? Yes
✔ Project initialized! ■ Template copied ■ Dependencies installed ■ Git initialized
next Liftoff confirmed. Explore your project!
Enter your project directory using cd ./astro-minimal Run bun run dev to start the dev server. CTRL+C to stop. Add frameworks like react or tailwind using astro add.
Stuck? Join us at https://astro.build/chat
╭─────╮ Houston:│ ◠ ◡ ◠ Good luck out there, astronaut! 🚀
ทำการเปิดโปรเจ็คด้วย VS Code ขึ้นมา จะเห็นว่า ตัว Project structure จะมีหน้าตาแบบนี้
tree -L 3 -I 'node_modules'
.├── README.md├── astro.config.mjs├── bun.lockb├── package.json├── public│ └── favicon.svg├── src│ └── pages│ └── index.astro└── tsconfig.json
ไฟล์สำคัญคือ
astro.config.mjs
- ไฟล์สำหรับตั้งค่าต่างๆ ของ Astrosrc/pages/index.astro
- ไฟล์ index สำหรับเป็นหน้าเว็บ ตัว Astro รองรับ file-based routing
ทำการ start server ขึ้นมา ด้วยคำสั่ง (ในบทความผมใช้ bun แต่ผู้อ่านสามารถเลือกใช้ npm/pnpm หรือ yarn ก็ได้)
bun dev
ตัว server จะทำการ start และเราจะได้หน้าเว็บที่ url : http://localhost:4321/
Pages
ใน Astro เราสามารถสร้าง pages ได้ด้วยการสร้างไฟล์ใน โฟลเดอร์ /src/pages
เราเริ่มด้วยการสร้างหน้า About ขึ้นมาก่อน ด้วยไฟล์ about.astro
---
---
<html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <meta name="viewport" content="width=device-width" /> <meta name="generator" content={Astro.generator} /> <title>About</title> </head> <body> <h1>นี่คือหน้า About</h1> </body></html>
จะเห็นว่าด้านบน มี ---
(code fence) เป็น formatter ที่ให้เราสามารถเรียกใช้ JavaScript กำหนดตัวแปร import file ต่างๆได้ เช่น
---const title = 'นี่คือหน้า About'---
<html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <meta name="viewport" content="width=device-width" /> <meta name="generator" content={Astro.generator} /> <title>About</title> </head> <body> <h1>{title}</h1> </body></html>
ตอนนี้หน้าเว็บเราก็จะมี 2 หน้า คือ หน้าแรก และหน้า About
การ navigate ก็เหมือน HTML tag เลยคือ
<ul> <li><a href="/">Home</a></li> <li><a href="/about">About</a></li></ul>
Routing

ตัว Astro จะใช้ file-based routing เพื่อทำการ generate URL ให้กับโปรเจ็คของเรา ขอแค่เรามีไฟล์อยู่ที่โฟลเดอร์ /src/pages/
Static Routes
การกำหนด static routes ก็คือขอแค่มีไฟล์ใน /src/pages
ก็จะกลายเป็น page ของเว็บเราเลย โดยที่ตัวไฟล์รองรับคือ .md
, .mdx
.astro
.js
.ts
และ .html
# Example: Static routessrc/pages/index.astro -> mysite.com/src/pages/about.astro -> mysite.com/aboutsrc/pages/about/index.astro -> mysite.com/aboutsrc/pages/about/me.astro -> mysite.com/about/mesrc/pages/posts/1.md -> mysite.com/posts/1
Dynamic Routes
การทำ dynamic routes คือการ generate หลายๆ page โดยใช้ filename ที่ตั้งชื่อภายใน []
เช่น /pages/posts/[post].astro
เวลาที่ Astro build static หน้า page ที่กำหนดแบบนี้จะทำการ generate ตอน build ซึ่ง เราก็ต้องใช้ร่วมกับ getStaticPaths()
ลองสร้างหน้า /posts/[post].astro
ขึ้นมา มีหน้าตาแบบนี้
---export function getStaticPaths() { return [ {params: {post: 'post-1'}}, {params: {post: 'post-2'}}, {params: {post: 'post-3'}}, ];}
const { post } = Astro.params---
<div> <h1>{post}</h1></div>
เวลาที่ build มันจะถูก generate เป็น 3 หน้า คือ
/posts/post-1
/posts/post-2
/posts/post-3
Astro Component
การสร้าง component ใน Astro จะคล้ายคลึงกับ JSX บางส่วน และตัว template คล้ายๆกับ Vue หรือ Svelte
ตัว Astro component จะมีโครงสร้างแบบนี้
---// Component Script (JavaScript)---<!-- Component Template (HTML + JS Expressions) -->
Component Script
Astro จะใช้ code fence (---
) เป็นตัวกำหนด component script คล้ายๆ frontmatter ใน Markdown เราใช้ component script เพื่อเขียน JavaScript เช่น
- การ import ไฟล์ Astro component อื่นๆ
- การ import component อื่นๆ ได้เช่น React, Vue, Svelte
- การ import data พวก JSON หรือการ fetch data จาก API
- กำหนดตัวแปรต่างๆ ที่จะใช้ใน template
Component Template
เป็นส่วน Template หรือ HTML ตัว Component จะใช้ Astro Template Syntax ที่มีความคล้ายคลึงกับ JSX Expressions รวมถึง Astro directive
ตัวอย่าง Component Template
---// Your component script here!import Counter from '../components/Counter.astro'import ReactCounter from '../components/CounterReact.jsx'
const items = await fetchItems()
// การเข้าถึง props ของ astro componentconst { title } = Astro.props;---
<!-- HTML comments supported! -->{/* JS comment syntax is also valid! */}
<Counter /><h1 class="title">{title}</h1>
<ReactCounter client:visible />
<ul> {items.map((data) => <li>{data.name}</li>)}</ul>
<style> .title { font-size: 2rem; }</style>
<script> console.log('javascript code')</script>
จะเห็นว่า ตัว Astro component สามารถใช้ component ทั้ง Astro และ React component ได้ ส่วนการ render items ที่เป็น array ก็จะคล้ายๆ JSX คือใช้ map()
ส่วนที่เราเห็น client:visible
ตรง ReactCounter
ตรงนี้เป็น directive สำหรับการ hydrate (ตัว Astro จะยังไม่ render ที่ฝั่ง Server และไปทำการ hydrate เพิ่มโค๊ด JavaScript ตรงนี้ที่ฝั่ง client)
ถ้าเราสังเกตดีๆ ตัว component จะมี 3ส่วน
- code fence เป็นส่วนที่เอาไว้กำหนด JavaScript code
- ส่วน markup เป็นส่วน แสดง HTML
- ส่วน CSS อยู่ภายใต้
<style>
นอกจากนี้ เราก็ยังสามารถกำหนด <script>
ลงไปใน Component ได้เหมือนกัน
ความต่างระหว่าง code fence ---
กับ <script>
คือ ใน code fence เป็น Server side code ในขณะที่ <script>
เป็น client side ครับ ดังตัวอย่างด้านล่าง
---// Server-side codeconst items = await fetchItems();const title = "Hello";---
<!-- Template --><h1>{title}</h1><ul> {items.map(item => <li>{item}</li>)}</ul>
<script> // Client-side code document.querySelectorAll('li').forEach(li => { li.addEventListener('click', () => { li.classList.toggle('active'); }); });</script>
การกำหนด รับ ส่ง props ใน Astro ก็คล้ายๆ React ครับ เช่น เรามี Button.astro
เวลาส่ง props ให้ก็จะแบบนี้
---import Button from '../components/Button.astro';---
<Button text="Click me!" color="primary" />
โดยที่ตัว Button.astro
กำหนดไว้แบบนี้
---// รับ propsinterface Props { text: string; color?: 'primary' | 'secondary';}
const { text, color = 'primary' } = Astro.props;---
<button class={color}> {text}</button>
<style> button { padding: 0.5rem 1rem; border-radius: 0.25rem; border: none; } .primary { background: #4c1d95; color: white; } .secondary { background: #e5e7eb; color: #1f2937; }</style>
การใช้งาน CSS
ใน Astro เราสามารถใช้งาน CSS ได้หลายแบบ เช่น
ทำการ import จาก ไฟล์ css
---import '../styles/global.css'---
กำหนด CSS ใน component ซึ่ง <style>
default จะเป็น scope เฉพาะ component อยู่แล้ว
------<h1>Hello</h1><style>h1 { color: red;}</style>
การกำหนด CSS แบบ global จะใช้ directive (attribute) is:global
<style is:global> .title { color: red; }</style>
กำหนด Layout
เราสามารถกำหนด Layout เพื่อลดการเขียนโค๊ดซ้ำๆ ได้ เช่น เรามีหน้า index
กับหน้า about
ที่มีโค๊ด HTML เราก็ย้ายไปเป็น RootLayout
ได้ สร้างไฟล์ RootLayout.astro
ขึ้นมา ในโฟลเดอร์ layout
<html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <meta name="viewport" content="width=device-width" /> <meta name="generator" content={Astro.generator} /> <title>Astro</title> </head> <body> <slot /> </body></html>
จะเห็นว่าใน body เราจะมี <slot />
เป็นเหมือนการ render children ของ JSX หรือหลักการ slot outlet ของ Vue/Svelte
ทีนี้ไฟล์ index.astro
และ about.astro
เราก็แค่ import RootLayout
มาใช้แบบนี้
---import RootLayout from '../layout/RootLayout.astro'
const title = 'หน้า index Astro'---
<RootLayout> <h1>{title}</h1></RootLayout>
และไฟล์ about.astro
---import RootLayout from '../layout/RootLayout.astro'
const title = 'นี่คือหน้า About'---
<RootLayout> <h1>{title}</h1></RootLayout>
UI Framework
ลองใช้งานร่วมกับ UI library เช่น React ซึ่งตัว Astro มีการจัดการ hydrate React component ฝั่ง client-side ให้เรา
ทำการรันคำสั่งเพื่อติดตั้ง โดยตัว astro จะติดตั้ง dependencies จะตั้งค่า astro.config.mjs
ให้เราเอง
npx astro add react
ไฟล์ config เราหลังจากติดตั้ง react
// @ts-checkimport { defineConfig } from 'astro/config'
import react from '@astrojs/react'
// https://astro.build/configexport default defineConfig({ integrations: [react()],})
ไฟล์ tsconfig.json
{ "extends": "astro/tsconfigs/strict", "include": [".astro/types.d.ts", "**/*"], "exclude": ["dist"], "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "react" }}
ลองสร้าง React Component ขึ้นมา
import { useState } from 'react'
export default function Counter({ initialCount = 0 }) { const [count, setCount] = useState(initialCount)
return ( <div> <h2>React Counter</h2> <p>Count: {count}</p> <button onClick={() => setCount(count - 1)}>-</button> <button onClick={() => setCount(count + 1)}>+</button> </div> )}
หน้า index.astro
ก็ทำการ import ReactCounter
ไปใช้ โดยกำหนด directive client:visible
---import RootLayout from '../layout/RootLayout.astro'import ReactCounter from '../component/ReactCounter.jsx'
const title = 'หน้า index Astro'---
<RootLayout> <h1>{title}</h1>
<ReactCounter initialCount={0} client:visible /></RootLayout>
Build
สุดท้าย ลองทำการ build project ตัว astro ถ้าเราไม่ได้กำหนด output mode เป็น server
มันจะทำการ build static ให้เรานะครับ
bun run build
จะได้ output แบบด้านล่าง
building client (vite)16:04:44 [vite] ✓ 28 modules transformed.16:04:44 [vite] dist/_astro/ReactCounter.iJIUJ5ZK.js 1.04 kB │ gzip: 0.61 kB16:04:44 [vite] dist/_astro/index.Dy6lLLXr.js 7.77 kB │ gzip: 3.00 kB16:04:44 [vite] dist/_astro/client.BA2GgrTr.js 177.92 kB │ gzip: 56.33 kB16:04:44 [vite] ✓ built in 381ms
generating static routes16:04:44 ▶ src/pages/about.astro16:04:44 └─ /about/index.html (+4ms)16:04:44 ▶ src/pages/posts/[post].astro16:04:44 ├─ /posts/post-1/index.html (+2ms)16:04:44 ├─ /posts/post-2/index.html (+1ms)16:04:44 └─ /posts/post-3/index.html (+1ms)16:04:44 ▶ src/pages/index.astro16:04:44 └─ /index.html (+6ms)16:04:44 ✓ Completed in 31ms.
16:04:44 [build] 5 page(s) built in 802ms16:04:44 [build] Complete!
สรุป
บทความนี้ก็เป็นเพียงแค่พื้นฐาน และก็ Overview ให้เห็นภาพรวมคร่าวๆ ของ Astro นะครับ ยังมีเรื่องอื่นๆ อีกหลายๆเรื่องที่ไม่ได้พูดถึง เช่น Middleware การจัดการ Markdown, Content Collections, Data fetching, Astro DB, SSG, SSR, Authentication และอื่นๆ สามารถไปลองอ่าน Docs หรือดูตัวอย่าง Template Theme เพิ่มเติมได้ครับ
อ่อ สุดท้าย ตัวบล็อก Devahoy ล่าสุด ก็ใช้ Astro เช่นกันครับ และก็กำลังพยายามเรียบเรียงเนื้อหา เป็นซีรีย์ Astro อยู่ครับ รอติดตามนะครับ
Happy Coding ❤️
- Authors
-
Chai Phonbopit
เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust