Why Svelte, the Compiler
runtime framework เทียบกับ compiler
หัวข้อที่มีชื่อว่า “runtime framework เทียบกับ compiler”UI framework ส่วนใหญ่เป็น runtime library คุณส่ง framework ไปที่ browser แล้ว framework นั้นคอยดูแล virtual DOM ตอน runtime, re-run component เมื่อ state เปลี่ยน, diff tree ใหม่กับ tree เก่า แล้วนำความต่างไปใช้ กลไกพวกนั้น — reconciler, scheduler — คือ code ที่ user ต้องดาวน์โหลดและ CPU ต้องรันทุกครั้งที่อัปเดต
Svelte ย้ายงานออกจาก browser ไปไว้ที่ build step Svelte compiler อ่าน component ของคุณล่วงหน้า หาให้ชัดว่า DOM node ไหนขึ้นกับ state ตัวไหน แล้ว emit JavaScript ธรรมดาที่อัปเดตเฉพาะ node เหล่านั้น ไม่มี virtual DOM ให้ส่ง ไม่มีการ diff ตอน runtime
flowchart TB
subgraph runtime["Runtime framework"]
ship["ship framework + components"] --> vdom["runtime keeps a virtual DOM"]
vdom --> diff["diff on every state change"]
end
subgraph svelte["Svelte (compiler)"]
build["compile components at build time"] --> emit["emit precise update code"]
emit --> tiny["ship tiny JS, no diffing"]
end ”ไม่มี virtual DOM” ให้ประโยชน์อะไร
หัวข้อที่มีชื่อว่า “”ไม่มี virtual DOM” ให้ประโยชน์อะไร”- bundle เล็กลง คุณไม่ต้องส่ง reconciler ไป Svelte component compile เป็น code แบบ imperative ที่ใกล้เคียงกับที่คุณจะเขียนเองเพื่ออัปเดต node เหล่านั้น จึงมี framework overhead ติดมาน้อย
- runtime ทำงานน้อยลง เมื่อ
countเปลี่ยน ไม่มี tree ให้สร้างใหม่และ diff — code ที่ generate ไว้อัปเดต text node เดียวที่แสดงcountการอัปเดตเลยเจาะจงโดยธรรมชาติ - indirection น้อยลง ช่องว่างระหว่าง “สิ่งที่ฉันเขียน” กับ “สิ่งที่รัน” นั้นเล็ก: output ของ compiler ใกล้เคียงกับที่คุณจะเขียนเอง
นี่คือเหตุผลที่บางครั้งเรียก Svelte ว่า “the disappearing framework” — ส่วนใหญ่ของตัวเอง compile หายไป เหลือแค่ logic ของคุณบวก helper layer บาง ๆ
จริง ๆ แล้ว compiler สร้างอะไรออกมา
หัวข้อที่มีชื่อว่า “จริง ๆ แล้ว compiler สร้างอะไรออกมา”ให้ component ที่มี let count = $state(0) และ <p>{count}</p> compiler เห็นแบบ static ว่า text node นี้ขึ้นกับ count แล้ว generate code ที่ (1) สร้าง <p> หนึ่งครั้ง และ (2) register ว่าเมื่อ count เปลี่ยน ให้อัปเดตเนื้อหาของ text node เฉพาะจุดนี้ — ไม่มีอย่างอื่น คูณด้วยทั้งแอปแล้วคุณจะได้การอัปเดตที่แตะเฉพาะสิ่งที่เปลี่ยน โดยไม่มี runtime bookkeeping มาคอยหาว่า อะไร เปลี่ยน
<script> let count = $state(0);</script>
<!-- The compiler knows this text node — and only this one — depends on count. --><button onclick={() => count++}>clicked {count} times</button>คุณเขียน component แบบ declarative ส่วน compiler เขียน code DOM แบบ imperative ให้ การแบ่งงานแบบนี้คือคุณค่าทั้งหมด