Config & Integrations
ไฟล์ config
หัวข้อที่มีชื่อว่า “ไฟล์ config”ทุกโปรเจกต์ Astro ตั้งค่าจาก astro.config.mjs ผ่าน defineConfig ครั้งเดียว มีไม่กี่ key ที่รับน้ำหนักส่วนใหญ่:
import { defineConfig } from 'astro/config';import mdx from '@astrojs/mdx';import sitemap from '@astrojs/sitemap';import react from '@astrojs/react';import cloudflare from '@astrojs/cloudflare';
export default defineConfig({ site: 'https://example.com', // absolute site URL (used for sitemap, canonical, RSS) base: '/docs', // path prefix if not served at the domain root output: 'static', // 'static' (default) or 'server' for on-demand adapter: cloudflare(), // runtime for on-demand rendering integrations: [mdx(), sitemap(), react()], vite: { /* escape hatch to raw Vite config */ },});siteคือ URL ที่ deploy จริง จำเป็นสำหรับอะไรก็ตามที่ต้องออก absolute URL (sitemap, canonical tag, RSS)baseตั้ง path prefix เมื่อเว็บอยู่ใต้ subpath (เหมือนคอร์สนี้อยู่ใต้/astro)outputเลือก rendering mode เริ่มต้นadapterให้ runtime เมื่อใช้ on-demand rendering (จากบทเรียน rendering)integrationsคือ list ของ plugin ที่ขยายความสามารถของ Astro
ระบบ integrations
หัวข้อที่มีชื่อว่า “ระบบ integrations”integration คือ plugin ทางการหรือของ community ที่ hook เข้ากับ build ของ Astro เพื่อเพิ่มความสามารถ — render UI framework, generate sitemap, เพิ่ม MDX support, ต่อ adapter คุณติดตั้งแล้วเพิ่มลงใน array integrations หลายตัวมีตัวติดตั้งแบบคำสั่งเดียวที่แก้ config ให้:
# astro add writes the import + integrations entry for you, and installs depsnpx astro add reactnpx astro add mdx sitemapintegration ทางการที่พบบ่อย (@astrojs/*): react / vue / svelte / solid / preact (UI framework island), mdx (component ใน Markdown), sitemap และ adapter ของแต่ละ platform (cloudflare, node, vercel, netlify) หมายเหตุ: @astrojs/rss ไม่ใช่ integration — เป็น helper package ที่ import ใน endpoint เพื่อ generate feed จึงไม่มี astro add rss และไม่ต้องใส่ใน array integrations
astro add ไม่ใช่คำสั่ง “เพิ่ม npm package อะไรก็ได้” แบบ generic — มันรู้จักแค่ integration ที่มัน ship recipe ให้เท่านั้น ตามที่ CLI help จริงบน 7.3.5 ที่ติดตั้งจริงโชว์:
$ npx astro add --help astro add [...integrations] [...adapters] UI Frameworks react astro add react preact astro add preact vue astro add vue svelte astro add svelte solid-js astro add solid-js lit astro add lit alpinejs astro add alpinejs Documentation Frameworks starlight astro add starlight SSR Adapters netlify astro add netlify vercel astro add vercel deno astro add deno cloudflare astro add cloudflare node astro add node Others tailwind astro add tailwind mdx astro add mdx markdoc astro add markdoc partytown astro add partytown sitemap astro add sitemapFor more integrations, check out: https://astro.build/integrationsอะไรที่ไม่อยู่ในลิสต์นั้น (community integration หรือ raw Vite plugin) ต้องเพิ่มด้วยมือเอง: npm install แล้วดันเข้า integrations (หรือ vite.plugins) เอง
Integration hooks: รันตอนไหนจริง ๆ
หัวข้อที่มีชื่อว่า “Integration hooks: รันตอนไหนจริง ๆ”integration คือ object ธรรมดาที่มี name กับ hooks map — แต่ละ hook fire ที่จุดเฉพาะใน lifecycle ของ Astro เอง สองตัวที่ใช้บ่อยที่สุด: astro:config:setup (ตอน Astro กำลัง resolve config ก่อนอะไรจะ build) กับ astro:build:done (หลัง build ทั้งหมดเสร็จ พร้อม output directory) พิสูจน์ด้วย integration จริง ๆ ตัวเล็ก ๆ กับ build จริง:
export default function myLogIntegration() { return { name: 'my-log-integration', hooks: { 'astro:config:setup': ({ logger }) => { logger.info('config:setup ran'); }, 'astro:build:done': ({ logger, dir }) => { logger.info(`build:done, output at ${dir}`); }, }, };}import { defineConfig } from 'astro/config';import myLogIntegration from './my-log-integration.mjs';
export default defineConfig({ integrations: [myLogIntegration()],});$ npx astro build --outDir dist[my-log-integration] config:setup ran[my-log-integration] build:done, output at file:///.../dist/client/ทั้งสองบรรทัดคือ logger output จริงจาก build จริง — astro:config:setup fire ครั้งเดียว แต่เนิ่น ๆ ตอน Astro ยังประกอบ config อยู่ (จุดที่ integration ส่วนใหญ่ register Vite plugin หรือ content collection); astro:build:done fire ครั้งเดียว ตอนจบเลย และส่ง dir มาให้ — ตำแหน่ง output จริง — ซึ่งตรงกับที่ integration ที่ต้องประมวลผล build output ต่อ (เขียน manifest, upload asset) รอคอยพอดี
Astro สร้างบน Vite
หัวข้อที่มีชื่อว่า “Astro สร้างบน Vite”ข้างใต้นั้น dev server และ build ของ Astro ขับเคลื่อนด้วย Vite — ตั้งแต่ Astro 7 คือ Vite 8 ที่ bundler default เป็น Rolldown ซึ่งเขียนใหม่ด้วย Rust เร็วกว่า Rollup 10–30 เท่าแต่ยังใช้ plugin API เดิม นั่นคือเหตุผลที่ dev server เริ่มทันที, HMR เร็ว และคุณใช้ plugin จาก ecosystem ของ Vite/Rollup ได้เกือบทั้งหมด (เช็กว่า plugin รองรับ Vite 8 ตอนอัปเกรด) เมื่อต้องการอะไรที่ Astro ไม่ได้เปิดให้ตรง ๆ ก็ลงไปที่ key vite แล้วตั้งค่า Vite เอง:
export default defineConfig({ vite: { plugins: [/* any Vite plugin */], resolve: { alias: { '@': '/src' } }, },});flowchart TB cfg["astro.config.mjs (defineConfig)"] --> integ["integrations: UI frameworks, mdx, sitemap, adapter"] cfg --> vite["vite: raw Vite 8 (Rolldown) plugins and config"] integ --> build["Astro build"] vite --> build
config เปลี่ยนอะไรใน Astro 7
หัวข้อที่มีชื่อว่า “config เปลี่ยนอะไรใน Astro 7”Astro 7 ทำให้หลายฟีเจอร์ stable ซึ่งแปลว่า flag experimental ที่คู่กัน หายไปแล้ว — ปล่อยทิ้งไว้จะเกิด config error ให้ย้ายขึ้นมาระดับบนสุด:
// Astro 6 // Astro 7experimental: { cache: { ... } } cache: { ... }experimental: { routeRules: { ... } } routeRules: { ... }experimental: { logger: { ... } } logger: { ... }experimental: { rustCompiler: true } // removed — the Rust compiler is the only compilerexperimental: { queuedRendering: true } // removed — now the defaultexperimental: { advancedRouting: true } // removed — src/fetch.ts is always onkey ระดับบนใหม่ที่ควรรู้: compressHTML ตอนนี้ default เป็น 'jsx' (กฎ whitespace แบบ JSX); fetchFile ย้ายหรือปิด entrypoint src/fetch.ts ที่ถูกจองชื่อ; markdown.processor ให้สลับ Sätteri เป็น unified() จาก @astrojs/markdown-remark และมี package หนึ่งที่หายไป: @astrojs/db ไม่ได้ดูแลต่อแล้ว — ใช้ node:sqlite, Drizzle หรือ database แบบ hosted (Turso, Neon, PlanetScale) แทน
สิ่งที่ยังอยู่ใต้ experimental ใน 7.3.5
หัวข้อที่มีชื่อว่า “สิ่งที่ยังอยู่ใต้ experimental ใน 7.3.5”ไม่ใช่ทุกอย่างย้ายขึ้นระดับบนสุด — บาง flag ยัง experimental จริง ๆ นี่คือลิสต์จริงครบถ้วน อ่านตรง ๆ จาก config schema ของ package ที่ติดตั้งจริง (node_modules/astro/dist/core/config/schemas/base.js) ไม่ใช่ blog post ที่อาจล้าสมัยไปแล้วตอนคุณอ่าน:
experimental: { clientPrerender, // pre-render <a> targets on hover/viewport-entry via speculation rules contentIntellisense, // editor JSON schemas for content collection frontmatter chromeDevtoolsWorkspace,// wires the dev server into Chrome DevTools' Workspaces feature incrementalBuild, // only rebuild what changed, for faster repeated builds svgOptimizer, // hook to run your own optimizer over inlined SVGs collectionStorage, // 'single-file' | 'chunked' | { type: 'chunked', chunkSize } — content store layout}ถ้า config snippet (ของคุณเองหรือ AI generate มา) ตั้ง experimental.rustCompiler, experimental.queuedRendering, หรือ experimental.advancedRouting มันล้าสมัยแล้ว — สามตัวนี้ถูกถอดออก ไม่ใช่เปลี่ยนชื่อ เพราะพฤติกรรมของมันเปิดเป็น default อยู่แล้ว (พิสูจน์ข้างบน) การตั้ง key ที่ไม่รู้จักใต้ experimental เป็น schema validation error จริง เพราะ schema ข้างบนคือ z.strictObject
🐛 หา bug ในโค้ดนี้
import { defineConfig } from 'astro/config';
export default defineConfig({experimental: { rustCompiler: true, contentIntellisense: true,},});เฉลย
contentIntellisense เป็นของจริงและยัง experimental อยู่ใน 7.3.5 — ส่วนนั้นไม่มีปัญหา rustCompiler ไม่ใช่: Rust compiler เลิกเป็น optional ตั้งแต่ Astro 7 ทำให้มันเป็นคอมไพเลอร์เดียวแล้ว flag เลยถูกลบไปพร้อมกับตัวเก่าที่ใช้ JS ที่มันเคยไว้สลับออก เพราะ experimental ถูก validate เป็น z.strictObject (verify แล้วจาก schema ที่ติดตั้งจริง) key ที่ไม่รู้จักแบบนี้จะทำให้ config validation ล้มเหลวจริง ๆ พิสูจน์ด้วย config เป๊ะ ๆ นี้กับ build จริง:
[config] Astro found issue(s) with your configuration:
! Invalid or outdated experimental feature. Check for incorrect spelling or outdated Astro version. See https://docs.astro.build/en/reference/experimental-flags/ for a list of all current experiments.ไม่ใช่ no-op เงียบ ๆ — build ปฏิเสธที่จะเริ่มเลย วิธีแก้ก็แค่ลบบรรทัด rustCompiler ทิ้ง
การเพิ่ม Tailwind (แบบปัจจุบัน)
หัวข้อที่มีชื่อว่า “การเพิ่ม Tailwind (แบบปัจจุบัน)”Tailwind ใน Astro ปัจจุบันติดตั้งเป็น Vite plugin (@tailwindcss/vite, Tailwind v4) — ไม่ใช่ integration @astrojs/tailwind แบบเก่า ตัวติดตั้งคำสั่งเดียวต่อให้เรียบร้อย:
npx astro add tailwindคำสั่งนั้นติดตั้ง plugin, เพิ่มลง Vite config และสร้าง CSS entry ให้ จากนั้น import CSS ครั้งเดียว (เช่นใน layout) แล้วใช้ Tailwind utility class ใน markup ได้ ถ้าเจอ guide ที่อ้างถึง integration @astrojs/tailwind นั่นคือวิธีเก่า วิธีปัจจุบันคือ Vite plugin ผ่าน astro add tailwind
Content-Security-Policy (security.csp)
หัวข้อที่มีชื่อว่า “Content-Security-Policy (security.csp)”เสถียรตั้งแต่ Astro 6.0, security.csp generate Content-Security-Policy จริงให้ทุกหน้า — hash inline script/style ของ Astro เองอัตโนมัติ จึงไม่ต้องดูแล hash list เองทุกครั้งที่ build output เปลี่ยน:
import { defineConfig } from 'astro/config';
export default defineConfig({ security: { csp: true, // or an object: { algorithm: 'SHA-512', directives: [...], scriptDirective: {...}, styleDirective: {...} } },});Astro ส่ง policy เดียวกันผ่านสองกลไกต่างกันขึ้นอยู่กับว่าหน้านั้น render ยังไง — พิสูจน์กับ build จริงที่มี security: { csp: true } หน้าแบบ on-demand (prerender = false หรือทั้งโปรเจกต์อยู่ใต้ output: 'server') ได้มันเป็น HTTP response header จริง เพราะมี response ที่มีชีวิตให้แนบ:
$ curl -sD - -o /dev/null http://127.0.0.1:4351/ | grep -i content-security-policycontent-security-policy: script-src 'self' 'sha256-mzyhaPlEXQku46rgza6MekztMi+pKj5BkQ4z75dLyDw=' 'sha256-BF0290pkb3jxQsE7z00xR8Imp8X34FLC88L0lkMnrGw=' ...; style-src 'self' ;หน้าแบบ prerendered (prerender = true หรือ default 'static') ไม่มี response ที่มีชีวิตให้แนบ header — directive เดียวกันเป๊ะถูกเขียนลงใน HTML ที่ build แล้วแทน เป็น tag <meta http-equiv>:
$ grep -o '<meta[^>]*content-security-policy[^>]*>' dist/client/static-about/index.html<meta http-equiv="content-security-policy" content="script-src 'self' 'sha256-mzyhaPlEXQku46rgza6MekztMi+pKj5BkQ4z75dLyDw=' ...; style-src 'self' ;">ทั้งสองมาจาก config security.csp ตัวเดียวกันเป๊ะและพก script/style hash ที่ generate เหมือนกันเป๊ะ — มีแค่กลไกการส่ง (header vs <meta>) ที่ต่างกัน เลือกอัตโนมัติต่อ route ตาม prerender ซึ่งเป็น setting เดียวกับที่บท SSR & adapters สอนไว้
อ่านโค้ดที่ AI เขียน
หัวข้อที่มีชื่อว่า “อ่านโค้ดที่ AI เขียน”astro.config.mjs ที่ AI generate มา — หรือคำตอบทั้งหมดแบบ “นี่คือวิธี config Astro” — ควรเช็คกับสิ่งที่ 7.3.5 validate จริง:
- มัน nest อะไรใต้
experimentalที่ไม่ใช่หนึ่งในหก flag จริงที่พิสูจน์ข้างบนหรือเปล่า (clientPrerender,contentIntellisense,chromeDevtoolsWorkspace,incrementalBuild,svgOptimizer,collectionStorage)? flag ที่ล้าสมัยจาก blog post Astro 4/5 เป็น startup error จริง พิสูจน์แล้วข้างบน ไม่ใช่แค่ warning - มันยัง nest
cache,routeRules, หรือloggerใต้experimentalอยู่หรือเปล่า? พวกนี้เป็น key ระดับบนสุดตั้งแต่ Astro 7 แล้ว - มันเพิ่ม
@astrojs/tailwindเข้าintegrationsหรือเปล่า? นั่นคือ integration ก่อนยุค Tailwind v4 Astro ปัจจุบันใช้ Vite plugin@tailwindcss/viteแทน ติดตั้งผ่านastro add tailwind - มันแนะนำ
@astrojs/dbสำหรับ database layer หรือเปล่า? มันถูก npm deprecate แล้ว — verify ตรง ๆ:npm view @astrojs/db deprecatedคืนค่า “This package is deprecated. Use a database client (Drizzle, Kysely, etc.) directly instead.” - hook ของ integration อ่าน
dir(output directory) ในastro:config:setupแทนที่จะเป็นastro:build:doneหรือเปล่า? พิสูจน์แล้วข้างบน:dirมีความหมายก็ต่อเมื่อ build ผลิต output จริงแล้วเท่านั้น —astro:config:setupรันก่อนอะไรจะ build เลยด้วยซ้ำ
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”astro.config.mjs ไม่ใช่ถุงรวม setting แบบแบน ๆ — มันใกล้เคียงกับคำอธิบาย pipeline มากกว่า: integrations (และ adapter) คือพฤติกรรมสำเร็จรูปที่ hook เข้ากับจุดเฉพาะใน build lifecycle (astro:config:setup, astro:build:done, … พิสูจน์แล้วข้างบนด้วย logging integration จริง) ในขณะที่ vite คือทางออกดิบที่อยู่ข้างใต้ทั้งหมดนั้น experimental เป็นพื้นที่ชั่วคราวที่ validate แบบ strictObject อย่างชัดเจน — พิสูจน์แล้วข้างบน key ที่ไม่รู้จักตรงนั้นจะล้มเหลวเสียงดัง ไม่ใช่ถูกเมินเงียบ ๆ ก็เพื่อให้ flag ที่ล้าสมัยจาก tutorial เก่าไม่ทำอะไรแบบเงียบ ๆ
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”Official integration (astro add x) |
Raw Vite plugin (vite.plugins) |
|
|---|---|---|
| การติดตั้ง | คำสั่งเดียว แก้ config ให้ | npm install ด้วยมือ + ต่อเข้า vite.plugins เอง |
| รู้จัก Astro ไหม | ใช่ — hook astro:config:setup, register content collection ได้ ฯลฯ |
ไม่ — เป็น Vite plugin API ธรรมดาเท่านั้น |
| ครอบคลุมอะไร | UI framework, MDX, sitemap, adapter, Tailwind | อะไรก็ได้ใน ecosystem ที่ compatible กับ Vite/Rollup |
| ความเสี่ยงตกรุ่น | ต่ำ — ดูแลตาม release cadence ของ Astro เอง | ขึ้นกับว่าคนเขียน plugin ตาม Vite 8/Rolldown ทันไหม |
ความเข้าใจผิดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อย”- “flag
experimentalทั้งหมดจาก tutorial Astro 4/5 ยังใช้ได้ อาจแค่มี warning” พิสูจน์แล้วข้างบน: keyexperimentalที่ไม่รู้จักเป็น config error จริงบน 7.3.5 (z.strictObject) ไม่ใช่ warning — build ปฏิเสธที่จะเริ่มเลย - “
astro addติดตั้ง npm package อะไรก็ได้เข้า config” พิสูจน์แล้วข้างบนด้วย--helpoutput จริง: มันรู้จักแค่ลิสต์ recipe ตายตัว อะไรนอกจากนั้นต้องnpm installเองบวกแก้ config เอง - “hook ของ integration อ่าน output
dist/ที่เสร็จแล้วได้ตั้งแต่ build เพิ่งเริ่ม” พิสูจน์แล้วข้างบน:astro:build:done— ไม่ใช่astro:config:setup— คือตัวที่ fire หลังจาก output มีอยู่จริง พร้อมdirจริงชี้ไปที่มัน - “
@astrojs/tailwindยังเป็นวิธีที่ถูกต้องในการเพิ่ม Tailwind” มันคือ integration ก่อนยุค v4 Astro ปัจจุบันastro add tailwindติดตั้ง Vite plugin@tailwindcss/viteแทน