HTMX เจ๋งมากจนผมสร้างมันขึ้นมาเอง (2024)
(dbushell.com)- HTMX คือแนวทางที่ยกระดับฟรอนต์เอนด์แบบค่อยเป็นค่อยไปโดยยึด HTML ที่เรนเดอร์จากเซิร์ฟเวอร์เป็นศูนย์กลาง และพัฒนาต่อจากวิธีการก่อนที่ JavaScript UI สมัยใหม่แบบ React จะขยายใหญ่เทอะทะ
- ระหว่างรีแฟกเตอร์เซิร์ฟเวอร์ของ podcast web app ที่โฮสต์เอง เมื่อลองแทนที่ SvelteKit ด้วย DinoSsr ก็พบปัญหาว่าการย้ายหน้าทำให้การเล่นเสียงหยุดลง และสามารถใช้ HTMX อัปเดตเฉพาะส่วน `` เพื่อคงคอมโพเนนต์เสียงไว้ได้
- แม้ HTMX จะถูกแจกจ่ายเป็นไลบรารีฝั่งฟรอนต์เอนด์ แต่ส่วนสำคัญของการติดตั้งใช้งานกลับอยู่ที่เทมเพลตฝั่งแบ็กเอนด์และการตั้งค่าเซิร์ฟเวอร์ โดยคำขอ HTTP ที่ส่งคืน HTML ทำหน้าที่เป็น HTMX API โดยพฤตินัย
- ตัวอย่างขั้นสูงที่มีแอตทริบิวต์
hx-*, ตัวอย่าง `` ที่คลิกได้ และ inline JavaScript จำนวนมาก สะท้อนข้อจำกัดของ declarative attribute template และเป็นจุดที่ผู้เขียนไม่ค่อยพอใจในเอกสาร - มินิอิมพลีเมนเทชันที่ผู้เขียนทำเองใช้
304caching, History API, การแทนที่ `` และการพรีโหลดด้วย pointer events เป็นองค์ประกอบหลัก และทำให้ codebase เล็กลงกับเรียบง่ายขึ้นด้วยการลด JavaScript ในเบราว์เซอร์
ตำแหน่งของ HTMX
- HTMX ปฏิเสธ JavaScript UI สมัยใหม่และเลือกใช้ HTML ที่เรนเดอร์จากเซิร์ฟเวอร์ โดยเป็นแนวทางที่ต่อยอดจากวิธีการก่อนที่ฟรอนต์เอนด์จะพองตัวจาก React
- infinite scroll และผลการค้นหาแบบเรียลไทม์เป็นตัวอย่างยอดนิยมของ HTMX และแม้ HTMX จะดูมีขอบเขตจำกัด ไม่ได้พยายามแก้ทุกปัญหาแบบที่ React และเครื่องมือคล้ายกันทำ แต่ภายในข้อจำกัดนั้นมันก็มีคุณค่าสูง
- HTMX ไม่ใช่ “magic bullet” และประเด็นสำคัญคือมุมมองที่ว่าแนวคิดบางอย่างเบื้องหลัง HTMX สำคัญกว่าตัว HTMX เอง
การทดลอง
- แค่อ่านเอกสารยังไม่พอ จึงลองใช้งานจริง และงานรีแฟกเตอร์เซิร์ฟเวอร์ของ podcast web app ที่โฮสต์เองก็เป็นเป้าหมายสำหรับการนำ HTMX มาใช้
- อิมพลีเมนเทชันเดิมใช้ SvelteKit ซึ่งแม้จะเป็นเครื่องมือที่ชอบ แต่ก็อาจหนักเกินไปสำหรับเว็บไซต์ขนาดเล็ก
- สิ่งที่นำมาแทนคือ DinoSsr ที่ผู้เขียนกำลังสร้างเอง และโปรเจกต์เดียวกันนี้ยังใช้สำหรับสร้าง static site และให้บริการบล็อกบุ๊กมาร์กด้วย
- DinoSsr ทำงานฝั่งเซิร์ฟเวอร์เป็นหลัก และแม้จะส่งมอบคอมโพเนนต์แบบฟรอนต์เอนด์ “islands” ได้ แต่โครงสร้างนี้ไม่ได้รองรับปฏิสัมพันธ์ระดับทั้งหน้า
- คอมโพเนนต์เครื่องเล่นเสียงต้องคงอยู่ระหว่างการย้ายหน้า และหากการย้ายหน้าทำให้ทั้งหน้าโหลดใหม่ ประสบการณ์การเล่นก็จะจบลงอย่างรวดเร็ว
- SvelteKit จัดการทั้งการอัปเดต UI และฟรอนต์เอนด์ routing แต่ในบิลด์ใหม่มีเพียงคอมโพเนนต์เครื่องเล่นเสียงเท่านั้นที่ใช้ JavaScript ฝั่งฟรอนต์เอนด์ และ DinoSsr ก็จงใจไม่พยายามทำ client-side routing
- บางหน้ามีเพียงส่วน `` ที่ต่างกัน ดังนั้นถ้าใช้ HTMX ยกระดับลิงก์แบบค่อยเป็นค่อยไปแล้วอัปเดตเฉพาะบริเวณนี้ ก็จะคงคอมโพเนนต์เสียงไว้ได้โดยไม่ต้องโหลดทั้งหน้าใหม่
- การใช้ HTMX ให้ผลลัพธ์ที่ดี แต่ไม่นานก็ถอด HTMX ออกแล้วเปลี่ยนเป็นเวอร์ชันมินิที่ทำเองจากแนวคิดเดียวกัน
ความเห็นบางอย่างเกี่ยวกับ HTMX
- ในการทดลองนี้ HTMX เป็นตัวแทนของ Svelte ฝั่งฟรอนต์เอนด์ แต่ไม่ใช่ตัวแทนแบบเสียบแทนได้ทันทีเหมือน React หากเป็นการเปลี่ยนวิธีคิดครั้งใหญ่
- HTMX ถูกแจกจ่ายเป็นไลบรารี JavaScript ที่ยกระดับฟรอนต์เอนด์แบบค่อยเป็นค่อยไป แต่การติดตั้งใช้งานส่วนใหญ่เกิดขึ้นที่แบ็กเอนด์ โดยต้องเตรียมเทมเพลตและการตั้งค่าเซิร์ฟเวอร์เอง
- คำขอ HTTP ที่ส่ง HTML กลับมาทำหน้าที่เป็น HTMX API โดยพฤตินัย และรายละเอียดอย่างการตั้งค่า HTTP headers ให้ถูกต้องเพื่อการแคชที่เหมาะสมก็สำคัญมาก
- แม้จะมีแอตทริบิวต์มาตรฐาน
data-*และdatasetอยู่แล้ว แต่การใช้แอตทริบิวต์พรีฟิกซ์hx-*ที่ไม่ใช่มาตรฐานก็เป็นจุดขัดใจเล็กน้อย - เอกสารของ HTMX มีตัวอย่าง `` จำนวนมาก และตัวอย่างด้านล่างคือโครงสร้างที่เมื่อผู้ใช้คลิก
divจะส่งคำขอPUTไปที่/messagesและโหลดผลลัพธ์กลับเข้าdivนั้น
Put To Messages
- มีคำวิจารณ์ว่าตัวอย่างที่ให้ผู้ใช้ต้องคลิกองค์ประกอบ `` นั้นไม่ใช่แนวทางที่พึงประสงค์
- ตัวอย่าง HTMX ขั้นสูงบางส่วนที่ใช้ inline JavaScript ก็ดูรกพอสมควร และแสดงให้เห็นข้อจำกัดของ declarative attribute template
- แม้จะมีข้อวิจารณ์เหล่านี้ HTMX ก็ยังมอบชุดความสามารถที่มีขอบเขตจำกัดแต่มีประโยชน์ และเป็นเครื่องมือที่ช่วยยกระดับแพตเทิร์นการออกแบบเว็บทั่วไปได้หลายแบบ
สร้างเอง
- หลังเพิ่ม HTMX ได้สำเร็จก็ถอดมันออกทันที แล้วทำเวอร์ชันมินิของตัวเองจากแนวคิดเดียวกัน
- เพื่อให้คำขอ fetch ที่แคชได้ทำงานได้ จึงใช้ HTTP headers
last-modified,if-modified-sinceและการตอบกลับ304 - สำหรับการผสานกับ history พื้นฐาน ใช้
pushStateและpopstate - มีการเพิ่มโค้ดสำหรับดึงและแทนที่องค์ประกอบ `` และฝังการพรีโหลดแบบ pointer events โดยได้รับแรงบันดาลใจจาก HTMX preload extension
- การพรีโหลดแบบ pointer events จะเริ่มคำขอ fetch ก่อนที่เหตุการณ์
clickจะเกิดขึ้น ทำให้ได้การปรับปรุงด้านประสิทธิภาพเล็กน้อย - ซอร์สโค้ด สำหรับการทดลองนี้ยังพื้นฐานมาก แต่ก็แสดงให้เห็นว่า JavaScript ที่จำเป็นจริง ๆ ในเบราว์เซอร์มีน้อยเพียงใด
- ไม่ว่าจะใช้ HTMX หรือแนวทาง “we have HTMX at home” ผลลัพธ์คือ codebase เล็กลงและเรียบง่ายขึ้นมาก
JavaScript ฝั่งฟรอนต์เอนด์
- เทมเพลตและคอมโพเนนต์เป็นสิ่งจำเป็นในทางปฏิบัติสำหรับการจัดระเบียบโค้ดและการนำกลับมาใช้ใหม่ เว้นแต่จะเป็นเว็บไซต์ที่เล็กมาก และสามารถทำฝั่งเซิร์ฟเวอร์ได้ด้วย PHP, Ruby, Go, JavaScript และอื่น ๆ
- ไม่จำเป็นต้องทำซ้ำโครงสร้างนี้ในเบราว์เซอร์ หรือทำมันเฉพาะในเบราว์เซอร์เท่านั้น แต่ความนิยมของ React และเครื่องมือคล้ายกันทำให้นักพัฒนาจำนวนมากเลิกตั้งคำถามนี้ไปแล้ว
- ความเหนื่อยล้าจาก JavaScript ฝั่งฟรอนต์เอนด์เป็นเรื่องจริง และสัมพันธ์กับประสบการณ์ที่แม้จะชอบเทมเพลตฝั่งเซิร์ฟเวอร์แบบเก่า แต่ก็ยังออกแบบ JS UI เกินความจำเป็นมาโดยตลอด
- ถึงจะมองว่า HTMX เองไม่ได้ยอดเยี่ยมมากนัก ปรัชญาของมันก็ยังเป็นแนวทางที่มีคุณค่ามากพอจะทำให้นักพัฒนา JavaScript สมัยใหม่ต้องอายได้
1 ความคิดเห็น
ความเห็นจาก Lobste.rs
เป็นการจับผิดเล็ก ๆ น้อย ๆ แต่ผมไม่ค่อยชอบที่ใช้แอตทริบิวต์ HTML แบบมีคำนำหน้า
hx-*ในจุดที่ควรใช้data-*Htmx รองรับคำนำหน้าdata-มานานแล้ว ถ้าประเด็นคือไม่ควรทำให้ผู้ใช้ต้องคลิก `` ก็การใช้hx-boostของ htmx กับแท็ก anchor และ form น่าจะดีที่สุด เพราะมันช่วยเสริมความสามารถให้แท็กพวกนี้อย่างถูกวิธีโดยอัตโนมัติ จึงหลีกเลี่ยงการใช้ div ที่คลิกได้ นี่เป็นโปรเจกต์ที่แสดงให้เห็นว่าเบราว์เซอร์ต้องการ JavaScript จริง ๆ น้อยแค่ไหน และต่อจากนี้การบำรุงรักษาก็น่าจะมีน้อยมาก รวมถึงแทบไม่ต้องมีแพตช์ความปลอดภัยเลยด้วย น่ายินดีด้วยHTMX เรนเดอร์ทุกอย่าง แบบนี้ไม่ได้ทำให้เซิร์ฟเวอร์ทำงานหนักเกินไปหรือ? ให้ความรู้สึกคล้าย ปัญหา CGI ที่เราเคยเจอเมื่อก่อน
ด้วยเหตุผลคล้ายกัน ผมใช้ alpine-ajax ซึ่งคล้าย htmx มาก
alpine-ajaxมากเหมือนกัน ถ้าจะเริ่มใหม่โดยมีโจทย์ว่า “เอาข้อดีของ SPA มาไว้บน SSR” ก็คงจะเลือกทางนั้น ยังไงเสียผมก็ใช้ alpine อยู่แล้วสำหรับฟีเจอร์ฝั่งไคลเอนต์แบบโต้ตอบได้ เพียงแต่ตอนนี้ในรีโพซิทอรีมี htmx อยู่เยอะแล้ว และผมก็ไม่ได้มีอะไรคาใจกับ htmx