- สรุปปัญหาที่พบหลังอัปเกรดเว็บแอปพลิเคชันเป็น Svelte 5 เวอร์ชันล่าสุด
- เกิดพฤติกรรมที่ไม่คาดคิดจากฟีเจอร์ deep reactivity และ lifecycle ที่เปลี่ยนไป
- แม้จะชื่นชอบ Svelte 3/4 มาเป็นเวลานาน แต่ต่อจากนี้ คงจะไม่เลือก Svelte สำหรับโปรเจกต์ใหม่
ความจำเป็นของความเร็ว
- ทีม Svelte พยายามปรับประสิทธิภาพผ่าน deep reactivity และทำให้ได้ประสิทธิภาพที่ดีขึ้น
- เดิมทีก็มอบประสิทธิภาพที่รวดเร็วผ่านกระบวนการคอมไพล์อยู่แล้ว และนี่เป็นจุดแข็งที่ทำให้แตกต่างจากเฟรมเวิร์กอื่น
- แม้สิ่งนี้จะทำให้เฟรมเวิร์กมีความทึบและดีบักได้ยาก แต่ก็รู้สึกว่าเป็น trade-off ที่ยอมรับได้ในด้านประสิทธิภาพและ productivity
ความจำเป็นของความเร็ว
- การเปลี่ยนแปลงหลักที่ทีม Svelte มุ่งเน้นใน Svelte 5 คือ “deep reactivity” ซึ่งมีเป้าหมายเพื่อเพิ่มประสิทธิภาพด้วย reactivity ที่ละเอียดขึ้น
- ใน Svelte เวอร์ชันก่อนหน้า เป้าหมายนี้ทำได้เป็นหลักผ่าน Svelte compiler
- จุดที่ไม่ต้องให้ผู้พัฒนามาเรียนรู้แนวคิดใหม่ด้วยตัวเอง และเอื้อต่อการจัดโครงสร้าง logic ภายในใหม่ได้ง่าย เป็นสิ่งที่ทำให้ความเป็นเอกลักษณ์ของ Svelte โดดเด่น
- ขณะเดียวกัน กระบวนการคอมไพล์นี้ก็ทำให้เฟรมเวิร์กทึบมากขึ้น และดีบักปัญหาซับซ้อนได้ยาก
- มีข้อผิดพลาดที่หาสาเหตุได้ยากเพราะเป็นบั๊กของ compiler เอง และบางครั้งแก้ได้ก็ต่อเมื่อรีแฟกเตอร์คอมโพเนนต์ที่มีปัญหาทั้งหมด
- ถึงอย่างนั้นก็ยังรู้สึกว่าเป็นการประนีประนอมที่สมเหตุสมผลในด้านความเร็วและ productivity จึงยอมรับความไม่สะดวกของการต้องรีเซ็ตโปรเจกต์เป็นระยะมาโดยตลอด
Svelte ไม่ใช่ Javascript
- Svelte 5 เพิ่ม trade-off นี้เป็น 2 เท่า
- ความต่างหลักคือจุดประนีประนอมระหว่าง abstraction กับประสิทธิภาพได้ลุกลามจากขั้นตอนคอมไพล์ไปถึงส่วน runtime แล้ว
- การใช้ Proxy เพื่อรองรับ deep reactivity
- สถานะ lifecycle ของคอมโพเนนต์แบบ implicit
- การเปลี่ยนแปลงสองอย่างนี้ช่วยปรับปรุงประสิทธิภาพ และทำให้ API สำหรับนักพัฒนาดู slicker มากขึ้น
- จะมีอะไรให้น่ารำคาญหรือ? น่าเสียดายที่ฟีเจอร์ทั้งสองนี้เป็นตัวอย่างคลาสสิกของ leaky abstraction
- ท้ายที่สุดแล้วมันทำให้สภาพแวดล้อมที่นักพัฒนาต้องรับมือนั้นซับซ้อนขึ้น
Proxies ไม่ใช่อ็อบเจ็กต์
- ด้วยการใช้ Proxy ทีม Svelte สามารถดันประสิทธิภาพของเฟรมเวิร์กให้ดีขึ้นอีกเล็กน้อยโดยไม่ต้องบังคับให้นักพัฒนาทำงานเพิ่ม
- ในเฟรมเวิร์กอย่าง React เวลาส่งสถานะผ่านหลายคอมโพเนนต์มักทำให้เกิด re-render ที่ไม่จำเป็นได้ง่าย ส่วน Svelte นำ Proxy มาใช้เพื่อลดปัญหานี้
- เดิมที Svelte compiler ก็หลีกเลี่ยงปัญหาบางอย่างที่อาจเกิดจากกระบวนการ diff ของ virtual DOM ได้อยู่แล้ว แต่ดูเหมือนว่าจะตัดสินใจว่า Proxy จะช่วยเพิ่มประสิทธิภาพได้อีก
- ทีม Svelte ยังกล่าวว่า Proxy ช่วยยกระดับประสบการณ์นักพัฒนาได้ด้วย พร้อมอ้างว่า “สามารถเพิ่มทั้งประสิทธิภาพและความง่ายในการใช้งานได้สูงสุด”
- ปัญหาคือแม้ Svelte 5 จะดูเรียบง่ายขึ้นภายนอก แต่ในความเป็นจริงกลับเพิ่ม abstraction มากขึ้น
- ตัวอย่างเช่น การใช้ Proxy เพื่อตรวจจับเมธอดของอาร์เรย์ทำให้ไม่ต้องเขียนโค้ดอย่าง
value = value แบบใน Svelte 4
- ใน Svelte 4 นักพัฒนาต้องเข้าใจการทำงานของ compiler อยู่พอสมควรเพื่อกระตุ้น reactivity ขณะที่ Svelte 5 ให้ความรู้สึกว่า “ลืม compiler ไปได้เลย” แต่ความจริงไม่ใช่แบบนั้น
- ความสะดวกที่ได้จาก abstraction ใหม่ มาพร้อมกับกฎที่นักพัฒนาต้องรู้เพิ่มขึ้นเพื่อให้ compiler ทำงานอย่างที่ต้องการ
- หลังใช้ Svelte มานาน โดยส่วนตัวค่อย ๆ หันไปใช้ Svelte store มากขึ้น และใช้ reactive declaration น้อยลง
- Svelte store มีพื้นฐานใกล้กับแนวคิดของ JavaScript มากกว่า วิธีเรียกเมธอด
update ก็ตรงไปตรงมา และไวยากรณ์ $ เป็นเพียงข้อดีเพิ่มเติม
- Proxy ก็เหมือนกับ reactive declaration ตรงที่ “ดูเหมือนเป็นสิ่งเดียวกัน แต่กลับทำงานต่างออกไปตรงจุดเชื่อมต่อจริง”
- ตอนเริ่มใช้ Svelte 5 ทุกอย่างดูเหมือนทำงานได้ดี แต่พอพยายามบันทึกสถานะของ Proxy ลง IndexedDB ก็เกิด
DataCloneError
- แถมถ้าจะตรวจให้แน่ใจว่าค่าใดเป็น Proxy ก็ต้องลองทำ structured clone ด้วย
try/catch ซึ่งมีต้นทุนด้านประสิทธิภาพสูง
- สุดท้ายจึงต้องคอยจำว่าอะไรเป็น Proxy และต้องใช้
$state.snapshot ทุกครั้งในบริบทภายนอกที่ไม่รู้จัก Proxy
- ผลลัพธ์คือ แทนที่ abstraction จะช่วยให้พัฒนาได้สะดวกขึ้นตามเจตนาเดิม กลับกลายเป็นว่าต้องมีกฎและขั้นตอนที่ซับซ้อนขึ้นสำหรับนักพัฒนา
คอมโพเนนต์ไม่ใช่ฟังก์ชัน
บทสรุป
- ของที่ใช้ง่ายนั้นมีเสน่ห์แน่นอน แต่ดังที่ Rich Hickey เคยพูดไว้ ความง่ายไม่ได้แปลว่าความเรียบง่ายเสมอไป
- เช่นเดียวกับที่ Joel Spolsky พูดไว้ ผู้เขียนไม่ชอบพฤติกรรมที่เกิดขึ้นอย่างไม่คาดคิด
- Svelte แสดงให้เห็น “เวทมนตร์” มามากตลอดที่ผ่านมา แต่ ในเวอร์ชันนี้ สิ่งที่ต้องท่องจำเพื่อใช้เวทมนตร์นั้นมีมากขึ้นจนภาระมากกว่าประโยชน์
- ประเด็นของบทความนี้ไม่ใช่เพื่อโจมตีทีม Svelte และผู้เขียนก็ตระหนักดีว่ามีคนจำนวนมากที่ชอบ Svelte 5 (และ React Hooks)
- สิ่งสำคัญคือความสมดุลระหว่างการมอบความสะดวกให้ผู้ใช้ กับการเปิดโอกาสให้ผู้ใช้ยังคงควบคุมสิ่งต่าง ๆ ได้
- ซอฟต์แวร์ที่ดีอย่างแท้จริงไม่ได้ตั้งอยู่บน “ความฉลาดแพรวพราว” แต่ตั้งอยู่บน “ความเข้าใจ”
- เมื่อเครื่องมือ AI พัฒนาขึ้น การเลือกใช้เครื่องมือที่ช่วยต่อยอดภูมิปัญญาที่สะสมมาและส่งเสริมความเข้าใจอย่างลึกซึ้ง ย่อมสำคัญกว่าเครื่องมือที่ทำให้เราไม่รู้ด้วยซ้ำว่ากำลังทำอะไรอยู่
- ขอขอบคุณ Rich Harris และทีมสำหรับประสบการณ์การพัฒนาที่สนุกตลอดมา และหวังว่าบทความนี้จะเป็นฟีดแบ็กที่ไม่ถึงกับคลาดเคลื่อนเสียทีเดียว
7 ความคิดเห็น
คนที่สร้าง
proxyจะทำงานได้สบาย แต่คนที่ต้องดีบักนี่หงุดหงิดเลยล่ะ 555โปรเจกต์ข้าง ๆ นี่ solidjs ได้อันดับหนึ่งเรื่อง DX >.< / แฮมบก
ผมคิดว่าเพราะมีทางเลือกอย่าง Svelte อยู่ React/nextjs เลยคงได้รับแรงกระตุ้นอย่างมากเช่นกัน
โดยพื้นฐานแล้ว Svelte คือ language ดังนั้นก็หวังว่ามันจะช่วยชี้ทิศทางได้อย่างชัดเจนว่าภาษาสำหรับอธิบาย UI ควรพัฒนาไปทางไหน
ส่วนตัวผมจะใช้ React
มากไปก็ไม่ดี
หมกมุ่นจนเสียสติ
ซ้ำซ้อนเกินจำเป็น
ผมคิดว่ามันได้รับอิทธิพลจาก React และโดยเฉพาะ Next อยู่ไม่น้อยจนเปลี่ยนไปในทางแปลก ๆ
+pageถ้าดูโดยไม่รู้จัก svelte มาก่อนก็เข้าใจได้ยาก และ rune อย่าง$state,$derivedก็ดูเหมือนกำลังเดินตาม React ซึ่งถ้าเป็นอย่างนั้นยุคที่ใส่$:ไว้หน้าตัวแปรยังดูดีกว่าเสียอีก ไวยากรณ์แบบเก่าอย่าง{#each a in array} {/each}ก็พอทนได้อยู่หรอก แต่ก็ยังน่ารำคาญเหมือนเดิม ถ้าจะบอกว่าการทำ reactivity แบบเลือกได้ช่วยให้ประสิทธิภาพดีขึ้น ผมว่า solidjs เป็นทิศทางที่ดีกว่ามาก แถมเพราะใช้ jsx ตรง ๆ เลยทำให้ย้ายมาจาก react ได้ง่ายกว่าเมื่อเทียบกันด้วย ที่ solidjs ยังไม่ได้รับความสนใจมากนักนี่ถึงขั้นน่าแปลกใจเลยครับดูเหมือนว่า Signals กำลังมุ่งหน้าไปสู่ Trough of Disillusionment ของ Gartner hype cycle นะครับ 🤔 พอกรณีการใช้งานค่อย ๆ ชัดเจนขึ้น ผมคิดว่าการประเมินก็น่าจะดีขึ้นได้
ความคิดเห็นบน Hacker News
ตอนแรกไม่ได้สนใจ runes เท่าไรนัก แต่พอเห็นว่าสามารถนำเข้าคอมโพเนนต์ภายนอกที่มี reactive เข้ามาในเทมเพลต
.svelteและห่อหุ้มความเป็น reactive ไว้ภายในได้ ก็เปลี่ยนความคิดไปเลย นั่นหมายความว่าสามารถเขียนเทสต์ด้วย vitest พร้อมกับยังได้ประโยชน์จาก reactivity ด้วย ซึ่งทรงพลังมาก และเท่าที่รู้ AFAIK นี่ค่อนข้างมีเอกลักษณ์ในโลกฟรอนต์เอนด์ผมกำลังพัฒนาแอป SvelteKit ที่นำไปใช้งานเชิงพาณิชย์อยู่จริง และอยากแชร์ความเห็นจากประสบการณ์
+pageคุณสามารถวางไฟล์ Svelte ไว้ตรงไหนก็ได้ตามต้องการ และมันก็เรนเดอร์ได้ลื่นไหลพร้อมกับยังได้ข้อดีของเฟรมเวิร์กสมัยใหม่น่าเสียดายที่ EmberJS ค่อยๆ หายไป API ของมันค่อนข้างเสถียรมาตลอด 10 ปีที่ผ่านมา พูดแบบย้อนแย้ง คนที่เขียนแอป EmberJS ตลอด 10 ปีที่ผ่านมาน่าจะเจอปัญหาการย้ายระบบน้อยกว่าคนที่ทำงานแบบเดียวกันด้วย React, Svelte, Vue ฯลฯ
ลิงก์ Github สองลิงก์ที่ผู้เขียนใส่ไว้ด้านบนของโพสต์ ชี้ไปที่ปัญหาเดียวกัน และปัญหานั้นมีวิธีแก้แล้ว (ใช้ $state.raw)
Svelte 5 ไม่ใช่ JavaScript ในแบบที่<i>แย่ที่สุด</i> มันไม่ได้แก้ปัญหาของ js แต่กลับให้ abstraction ที่รั่วสำหรับปัญหาฟรอนต์เอนด์บางอย่าง ผมคิดว่า Solid เป็นทิศทางที่ดีกว่า Svelte เพราะ Solid ไม่ได้พึ่งพาภาษาใหม่ แต่ก็เข้าใจสัญชาตญาณที่อยากสร้างสิ่งที่ไม่ใช่ js เพราะ js เองก็ไม่ใช่สิ่งที่เหมาะที่สุด Elm คือผู้มาก่อนของ svelte แต่ผมคิดว่าเราสร้างสิ่งที่ดีกว่านั้นได้...[0]
ผมเริ่มใช้ Svelte 5 เพราะเกลียด store ใน Svelte 4 มาก ตอนนี้กำลังใช้ Sveltekit กับ Svelte 5 ในโปรเจกต์ใหม่ และต้องบอกว่า... ประสิทธิภาพการทำงานของ React ยังไร้เทียมทาน แม้ว่าเทคโนโลยีของ Sveltekit และ Svelte จะดีกว่าในเชิงเทคนิคก็ตาม
+page.server.tsหรือ+page.svelteหรือรูปแบบใกล้เคียงกัน ทำให้ค้นหาโค้ดได้ยาก เครื่องมือของ Svelte ก็แยกออกจากtscและ ESLint ทำให้รวมเข้ากับ CI และใช้ในขั้นตอนพัฒนาได้ยากกว่าการบอกว่า Svelte ไม่ใช่ JavaScript เพราะมีพฤติกรรมไม่คาดคิดตอนส่ง closure ผ่าน callback ฟังดูแปลกๆ ชื่อที่ดีกว่าน่าจะเป็น "Svelte 5 ทำให้ผมตกใจและเลยไม่ชอบมัน" มากกว่า
ถ้าคุณกำลังมองหาไลบรารีสำหรับสร้างคอมโพเนนต์และแอปที่ใช้ JavaScript ล้วนได้ ลองดู Lit: Lit
ถ้าคุณต้องการประสบการณ์ฟรอนต์เอนด์ที่สมเหตุสมผล? ใช้ vanilla Javascript, web components, htmx, Blazor