Performance & Deploy
เร็วโดย default
หัวข้อที่มีชื่อว่า “เร็วโดย default”เพราะ Svelte เป็น compiler (บทเรียนแรกสุด) performance baseline จึงดีอยู่แล้ว — ไม่มี virtual DOM runtime ให้ ship และ component compile เป็น code update ที่พุ่งตรงจุด คุณเสียเวลาสู้กับ bundle size น้อยกว่าแอปที่ใช้ runtime framework มาก — overhead ของ framework ที่ติดมากับ code คุณมีน้อย
แต่นั่นไม่ได้ทำให้ performance เกิดขึ้นเอง code ของคุณเอง, image, library ภายนอก และการเลือก data-loading ยังสำคัญ แต่จุดเริ่มต้นนั้นเบา และเครื่องมือด้านล่างช่วยให้คุณรักษาข้อได้เปรียบนั้นไว้
วัดสิ่งที่คุณ ship
หัวข้อที่มีชื่อว่า “วัดสิ่งที่คุณ ship”ก่อน optimize ต้องวัด vite build รายงานขนาด output และ ecosystem ของ Vite มี bundle visualizer (เช่น rollup-plugin-visualizer) ที่โชว์ว่าอะไรกินพื้นที่ — ปกติคือ dependency ภายนอกที่หนัก ไม่ใช่ตัว Svelte เอง
npm run build # vite build — prints chunk sizesระวัง: date/utility library ตัวใหญ่ที่ import มาทั้งก้อน, icon set ที่ดึงมาทั้งชุด หรือ component ที่ load แบบ eager ทั้งที่ทำ lazy ได้ SvelteKit code-split ต่อ route ให้อัตโนมัติ หน้าที่หนักจึงไม่ถ่วงส่วนที่เหลือของแอป
adapter: เลือก deploy target
หัวข้อที่มีชื่อว่า “adapter: เลือก deploy target”แอป SvelteKit deploy ผ่าน adapter ที่แปลง build ให้แพลตฟอร์มเฉพาะ คุณตั้งใน svelte.config.js:
import adapter from '@sveltejs/adapter-auto';
export default { kit: { adapter: adapter(), },};| adapter | deploy ไปที่ |
|---|---|
adapter-auto | ตรวจจับแพลตฟอร์มยอดนิยม (Vercel, Netlify, Cloudflare…) อัตโนมัติ |
adapter-node | Node server ที่รันยาว ๆ ที่คุณ host เอง |
adapter-cloudflare | Cloudflare Pages / Workers (edge) |
adapter-static | เว็บ static ล้วน — HTML/CSS/JS บน CDN ใดก็ได้ |
adapter-auto คือ default แบบ zero-config คุณเปลี่ยนไปใช้ adapter เฉพาะเมื่อรู้ target ของคุณ (และต้องใช้ option ของตัวเอง)
flowchart LR build["vite build (SvelteKit)"] --> adapter["adapter"] adapter --> node["Node server"] adapter --> edge["Cloudflare / edge"] adapter --> static["static CDN"]
static vs server
หัวข้อที่มีชื่อว่า “static vs server”การตัดสินใจเรื่อง deployment สะท้อนการเลือก rendering:
- static — ด้วย
adapter-static(หรือexport const prerender = trueบน route ที่ prerender ได้) หน้าถูก build เป็น HTML ตอน build time และ serve จาก CDN ใดก็ได้ เหมาะกับ content ที่เหมือนกันสำหรับทุกคน ไม่ต้องมี server - server — ด้วย runtime adapter (node/cloudflare/…) หน้า render on demand สำหรับ data ตอน request-time (ผู้ใช้ที่ login อยู่, data สด) ผสมได้: prerender หน้า marketing, render dashboard on demand
ตั้ง export const prerender = true บน route ที่เป็น static ได้ แล้วปล่อยที่เหลือให้ render on demand — วิธี hybrid เดียวกันที่ทำให้เว็บส่วนใหญ่ cache ได้ ส่วนอีกไม่กี่ route ยังคง dynamic
svelte/reactivity: อีกเหตุผลที่ output เล็กอยู่เสมอ
หัวข้อที่มีชื่อว่า “svelte/reactivity: อีกเหตุผลที่ output เล็กอยู่เสมอ”theme “เล็กโดย default” ของบทนี้ไม่ได้มีแค่เรื่อง compiler — มันขยายไปถึง standard library ที่ Svelte ship มาคู่กันด้วย svelte/reactivity (SvelteMap, SvelteSet, SvelteDate และเพื่อน ๆ) ให้ reactive collection แบบ rune-native โดยไม่ต้องหยิบ writable store มาห่อ ซึ่งสำคัญตรงนี้เพราะมันคือของที่มี shape เป็น dependency น้อยลงหนึ่งชิ้นที่ติดมาใน bundle สำหรับงานที่ runtime ทำให้แบบ native อยู่แล้ว บท Built-in Stores ของโมดูล state & stores พิสูจน์ความต่างของ reactivity เทียบกับ Map ธรรมดาแบบละเอียด takeaway ของบทนี้ง่ายกว่านั้น: การหยิบเวอร์ชัน rune-native แทนเวอร์ชันที่ห่อด้วย store เองก็เป็นทางเลือกด้าน performance เล็ก ๆ ที่ได้มาฟรี ไม่ใช่ trade-off
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”ทุกอย่างในบทนี้สืบเนื่องมาจากข้อเท็จจริงหนึ่งที่วางไว้ตั้งแต่บทเรียนแรกสุดของคอร์สนี้: compiler ของ Svelte ทำการวิเคราะห์ที่แพงไปแล้วตอน build time ดังนั้น “งาน performance” ที่นี่ส่วนใหญ่คือการ ไม่ทำลาย ข้อได้เปรียบนั้น — ไม่ import dependency ที่หนักมาทั้งก้อน, ไม่เลือก server adapter สำหรับ content ที่ static ได้ (และดังนั้น serve ฟรีจาก CDN โดยไม่ต้องใช้ compute เลย), ไม่หยิบ collection ที่ห่อด้วย store ในที่ที่ rune-native ตัวหนึ่งทำงานเดียวกันได้โดยมีของรอบ ๆ น้อยกว่า ไม่มีเครื่องมือไหนในบทนี้ (bundle visualizer, adapter, svelte/reactivity) สร้างความเร็วขึ้นมาเอง ส่วนใหญ่มันแค่ป้องกันไม่ให้คุณใช้ budget ที่ compiler มอบให้ฟรีไปโดยไม่ตั้งใจ
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ได้อะไร | ประโยชน์ | ต้นทุน |
|---|---|---|
adapter-auto | zero-config ทำงานได้เลยบนแพลตฟอร์มยอดนิยม | ไม่เปิด option เฉพาะแพลตฟอร์ม (การจัดการ env, edge binding) ที่ adapter เฉพาะให้ได้ |
adapter-static / prerender = true | host ฟรีบน CDN ใดก็ได้ ไม่มีต้นทุน compute ต่อ request เลย (พิสูจน์ด้วยการตรวจ build จริงในโมดูล SvelteKit) | serve data ตอน request-time ไม่ได้ — route ที่ต้องการผู้ใช้ที่ login อยู่หรือ data สด prerender ไม่ได้เลย |
adapter-node | ควบคุมเต็มที่ รันได้ทุกที่ที่ Node รัน (พิสูจน์ด้วย build จริงในโมดูล SvelteKit) | คุณเป็นเจ้าของ process เอง — env var อย่าง ORIGIN พิสูจน์แล้วในบทนั้น แทนที่จะมี platform จัดการให้ |
svelte/reactivity แทน store wrapper | rune-native ไม่มี code รูปร่าง dependency เพิ่มสำหรับ behavior เดียวกัน | ครอบคลุมแค่ type ของ collection ที่มัน ship มา (Map, Set, Date, URL, …) — shape reactive ที่ custom เต็มรูปแบบยังต้องใช้ $state/store |
ความเข้าใจผิดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อย”- “prerendering (
adapter-static/prerender = true) เป็นแค่ toggle ด้าน performance ที่เปิดบน route ไหนก็ได้” มันคือข้อจำกัดเชิงความถูกต้อง ไม่ใช่แค่เรื่องความเร็ว — route ที่ prerender ถูก render ครั้งเดียวตอน build time ดังนั้น route ที่ต้องการ data ตอน request-time (cookie, ผู้ใช้ที่ login อยู่, data สด) prerender ไม่ได้เลย ไม่ว่าจะเร็วขึ้นแค่ไหนก็ตาม - “
$env/static/privateกับ$env/dynamic/privateใช้แทนกันได้ — เลือกอันไหนก็ได้” มันถูก resolve คนละเวลากันเลย (build time เทียบ request time) — พิสูจน์ด้วย output จริงของ build ที่ตรวจสอบในโมดูล SvelteKit; การสลับตัวหนึ่งเป็นอีกตัวเปลี่ยนแบบเงียบ ๆ ว่าค่านั้นเปลี่ยนได้เมื่อไรโดยไม่ต้อง rebuild - “ใช้
SvelteMapจากsvelte/reactivityแทนMapธรรมดาที่ห่อด้วย store เป็นแค่ความชอบสไตล์” มันคือความต่างจริงด้าน bundle และ reactivity-model — ไม่มีกลไก subscription ของ store, reactivity ระดับ method แทนการแทนที่ทั้ง object — ครอบคลุมเต็ม ๆ ในบท built-in-stores ที่ลิงก์ไว้ด้านบน
อ่านโค้ดที่ AI เขียน
หัวข้อที่มีชื่อว่า “อ่านโค้ดที่ AI เขียน”เวลารีวิว code ที่เกี่ยวกับ deployment และไวต่อ performance ให้เช็ค:
export const prerender = trueถูกตั้งบน route ที่อ่าน data ตอน request-time จริง ๆ หรือเปล่า (cookies(), session,url.searchParamsที่ใช้เพื่ออะไรอื่นนอกจาก static generation) — เป็น correctness bug ไม่ใช่แค่ optimization ที่พลาด?- server code อ่าน secret ผ่าน
$env/dynamic/privateทั้งที่$env/static/privateพอแล้ว (และจะถูก inline เร็วกว่าเล็กน้อย) — หรือกลับกัน อ่าน$env/static/privateสำหรับค่าที่ตั้งใจให้เปลี่ยนได้ต่อ deployment โดยไม่ต้อง rebuild จริง ๆ หรือเปล่า? Map/Setถูกห่อด้วยwritablestore ที่เขียนเอง ทั้งที่SvelteMap/SvelteSetของsvelte/reactivityให้ reactivity เดียวกันด้วย code น้อยกว่าและไม่มี overhead ของ store subscription หรือเปล่า?- adapter เฉพาะ (
adapter-node,adapter-cloudflare) ถูกสมมติว่า “ทำงานได้เลย” เหมือนadapter-auto— พลาด setup เฉพาะแพลตฟอร์ม (env var, binding) ที่ adapter เฉพาะนั้นต้องการจริง ๆ พิสูจน์แล้วในโมดูล SvelteKit สำหรับORIGINของadapter-nodeหรือเปล่า?
🐛 หา bug ในโค้ดนี้
export const prerender = true;
export async function load({ cookies }) {const session = cookies.get('session_id');return { user: await getUserBySession(session) };}เฉลย
prerender = true บอก SvelteKit ให้ render route นี้ ครั้งเดียว ตอน build time แล้ว serve HTML แบบ static เดียวกันให้ทุกคนตลอดไป — แต่ load function อ่าน cookies.get('session_id') ค่าที่ต่างกันสำหรับผู้เยี่ยมชมแต่ละคนและมีอยู่แค่ตอน request time เท่านั้น ไม่มี “request” ระหว่าง build เลย ดังนั้นนี่ไม่ใช่ trade-off ด้าน performance ที่ subtle — มันคือ route ที่ทำสิ่งที่ตั้งใจไม่ได้เชิงโครงสร้าง ขึ้นอยู่กับสิ่งที่อ่านเป๊ะ ๆ prerendering ของ SvelteKit จะทำให้ build fail ด้วย error หรือ (แย่กว่านั้น) bake data ของ request ใดหนึ่งลงใน static output แบบเงียบ ๆ ที่ผู้เยี่ยมชมทุกคนหลังจากนั้นได้รับเหมือนกันหมด
วิธีแก้คือลบ prerender ออกไปเลย (หรือตั้งเป็น false) — dashboard ต่อผู้ใช้แต่ละคนคือฝั่ง “server” ของการแบ่ง static-vs-server ที่บทนี้อธิบายเป๊ะ ไม่ใช่ตัวเลือกของฝั่ง “static” ไม่ว่า static จะเร็วกว่าแค่ไหนก็ตาม