ข้ามไปยังเนื้อหา

Why Svelte, the 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
งานเกิดที่ไหน: runtime เทียบกับ compile time
  • 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 บาง ๆ

ให้ 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 ให้ การแบ่งงานแบบนี้คือคุณค่าทั้งหมด

runtime framework ทำงาน diffing ที่ไหน?
ทำไม bundle ของ Svelte ถึงเล็กโดยทั่วไป?
เมื่อ `count` เปลี่ยนใน Svelte เกิดอะไรขึ้น?
ทำไมถึงเรียก Svelte ว่า "the disappearing framework"?