2 คะแนน โดย GN⁺ 2025-11-04 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • htmx 4.0 เป็นเวอร์ชันรีบิลด์ครั้งใหญ่ที่ยกเครื่องจากสถาปัตยกรรมเดิมที่อิง XMLHttpRequest ไปเป็น สถาปัตยกรรมอะซิงโครนัสที่ยึด fetch() เป็นศูนย์กลาง ทั้งหมด
  • วิธีการ สืบทอดแอตทริบิวต์ เปลี่ยนจากแบบ implicit ไปเป็น การประกาศแบบ explicit ด้วย :inherited ช่วยให้โค้ดชัดเจนและดูแลรักษาง่ายขึ้น
  • แคชประวัติ ถูกทำให้ง่ายขึ้นจากการเก็บสแนปช็อตไว้ในเครื่อง ไปเป็น การกู้คืนผ่านคำขอเครือข่าย เพื่อเพิ่มความเสถียร
  • ฟีเจอร์ใหม่หลากหลายอย่าง เช่น streaming response, SSE, DOM morphing, แท็ก <partial>, View Transition queue ถูกผนวกรวมเข้าไปในคอร์
  • ในระยะยาวจะมุ่งไปที่ การทำให้โค้ดเบสเรียบง่ายขึ้นและขยายต่อได้ดีขึ้น พร้อมรองรับผู้ใช้ 2.0 ต่อไป

ภาพรวมของ htmx 4.0

  • htmx 4.0 เป็นการเขียนโครงสร้างเดิมใหม่ทั้งหมด โดยสะท้อนทั้ง ประสบการณ์พัฒนา fixi.js และ บทเรียนจากการดูแลรักษามา 5 ปี
  • การเปลี่ยนแปลงหลักสรุปได้เป็น 3 แกนสำคัญ ได้แก่ การเปลี่ยนไปใช้ fetch(), การทำให้การสืบทอดแอตทริบิวต์เป็นแบบ explicit และการทำให้แคชประวัติง่ายขึ้น

The fetch()ening

  • เปลี่ยนจาก XMLHttpRequest มาเป็น fetch() เพื่อทำให้โครงสร้าง Ajax ทันสมัยขึ้น
    • กรณีใช้งานส่วนใหญ่แทบไม่ได้รับผลกระทบมากนัก แต่ โมเดลเหตุการณ์ จะเปลี่ยนไปตามธรรมชาติแบบอะซิงโครนัสของ fetch()
  • การเปลี่ยนนี้เป็นฐานสำหรับการทำให้โค้ดง่ายขึ้นและรองรับการขยายฟีเจอร์ในอนาคต

การสืบทอดแอตทริบิวต์แบบ explicit

  • จากเดิมที่เป็นการสืบทอดแบบ implicit เปลี่ยนมาใช้ตัวขยาย :inherited เพื่อประกาศอย่างชัดเจน
    • ตัวอย่าง:
      <div hx-target:inherited="#output">
          <button hx-post="/up">Like</button>
          <button hx-post="/down">Dislike</button>
      </div>
      
    • หากไม่ได้ระบุ hx-target อย่าง explicit อิลิเมนต์ลูกจะไม่สืบทอดค่า
  • นี่คือ ความเปลี่ยนแปลงด้านอัปเกรดที่ใหญ่ที่สุด สำหรับผู้ใช้ส่วนใหญ่

การทำให้แคชประวัติง่ายขึ้น

  • แคช DOM snapshot ในเครื่อง ของ htmx 2.0 ไม่เสถียรนัก เพราะได้รับผลจากการแก้ไขโดย third party, state ที่ซ่อนอยู่ และปัจจัยอื่น ๆ
  • ใน 4.0 เปลี่ยนเป็น การกู้คืนผ่านคำขอเครือข่าย
    • การขยายความสามารถของแคชยังมีให้แบบเลือกเปิดใช้ (opt-in)
  • ช่วยให้โค้ดเบสง่ายขึ้นและ เพิ่มความน่าเชื่อถือของพฤติกรรมเริ่มต้น

ส่วนที่ยังคงเดิม

  • API หลัก เช่น hx-get, hx-post, hx-target, hx-boost, hx-swap, hx-trigger ยังคงเหมือนเดิม
  • นอกจากการเพิ่ม :inherited แล้ว โปรเจกต์ส่วนใหญ่ยังสามารถ ทำงานกับโค้ดเดิมได้ตามเดิม
  • ช่วยเสริมความสามารถในการบำรุงรักษาระยะยาวและความเสถียร

นโยบายการอัปเกรด

  • ผู้ใช้ 2.0 ต้องมีโปรเจกต์อัปเกรด แต่ 2.0 จะยัง ได้รับการสนับสนุนถาวร
  • 4.0 จะ เผยแพร่ควบคู่กับ 2.x และคาดว่าจะสลับเป็น latest ใน ต้นปี 2027
  • มีแผนให้ extension สำหรับคืนพฤติกรรมแบบ 2.0

สรุปฟีเจอร์ใหม่

การรวม streaming response และ SSE

  • ใช้ความสามารถ Readable Streams ของ fetch() เพื่อให้สามารถ สลับ DOM แบบบางส่วน ได้
  • SSE (Server Sent Events) ถูก นำกลับมารวมเป็นความสามารถหลักอีกครั้ง เพื่อรองรับการอัปเดตคอนเทนต์แบบค่อยเป็นค่อยไป

Morphing Swap

  • อิงกับ อัลกอริทึม Idiomorph เพื่อปรับปรุงการตัดสินใจ เก็บหรือลบโหนด ระหว่างการรวม DOM
  • morphInner, morphOuter swap ถูกรวมไว้ในคอร์

รองรับแท็ก <partial>

  • ทำให้ไวยากรณ์ที่ซับซ้อนของ Out-of-band swap เดิมง่ายขึ้น
  • <partial> เป็นองค์ประกอบเทมเพลตที่สามารถใช้แอตทริบิวต์มาตรฐานอย่าง hx-target, hx-swap เป็นต้น
  • Out-of-band swap จะย้อนกลับไปเป็น การแทนที่แบบง่ายที่อิง id

การปรับปรุง View Transition

  • เพิ่ม transition queue เพื่อป้องกันการชนกันของภาพการเปลี่ยนผ่าน
  • การเปลี่ยนผ่านด้วย CSS ยังคงใช้วิธีเดิม แต่ ทำให้รันไทม์อะซิงโครนัสง่ายขึ้น
  • ยังไม่ตัดสินใจว่าจะเปิดใช้งานเป็นค่าเริ่มต้นหรือไม่

การทำให้ลำดับเหตุการณ์เสถียรขึ้น

  • โครงสร้างอะซิงโครนัสที่อิง fetch() ทำให้ รับประกันลำดับเหตุการณ์ได้ง่ายขึ้น
  • มีรูปแบบการตั้งชื่อเหตุการณ์ใหม่:
    htmx:<phase>:<system>[:<optional-sub-action>]
    • ตัวอย่าง: htmx:before:request

การขยายความสามารถที่ดีขึ้น

  • รองรับ extension แบบ preload และ optimistic update บนพื้นฐาน async
  • เปิดวงจร request/response/swap ให้ นักพัฒนา extension ใช้งาน และสามารถสลับ implementation ของ fetch() ได้
  • ทำให้สามารถสร้างพฤติกรรมที่ต้องการได้โดยไม่ต้องพึ่ง hack

การปรับปรุง hx-on

  • ใช้ไวยากรณ์มาตรฐาน hx-on:<event name>
  • รองรับ API แบบอะซิงโครนัส จึงสามารถ เขียน DOM scripting แบบง่าย ๆ ได้
    <button hx-post="/like"
            hx-on:htmx:after:swap="await timeout('3s'); ctx.newContent[0].remove()">
        Get A Response Then Remove It 3 Seconds Later
    </button>
    
  • ทำงานได้แม้ในสภาพแวดล้อมที่ปิด eval() แต่บางความสามารถจะถูกจำกัด

ทิศทางโดยรวม

  • htmx 4.0 ตั้งเป้าที่จะ ลดบั๊กและเพิ่มความสามารถ โดยยังคง ประสบการณ์ใช้งานใกล้เคียงกับ 2.0
  • ด้วยโค้ดที่เรียบง่ายขึ้น โครงสร้างที่ explicit และการขยายความสามารถบนพื้นฐานอะซิงโครนัส ทำให้ได้ htmx ที่เสถียรและทันสมัยยิ่งขึ้น

กำหนดการพัฒนา

  • อัลฟ่าเวอร์ชัน (htmx@4.0.0-alpha1) เปิดเผยแล้วในตอนนี้
  • รีลีสทางการ 4.0.0 คาดไว้ช่วงต้นถึงกลางปี 2026
  • มีแผนสลับเป็น latest ใน ต้นปี 2027
  • ติดตามความคืบหน้าการพัฒนาได้ที่ GitHub branch four และ four.htmx.org

1 ความคิดเห็น

 
GN⁺ 2025-11-04
ความคิดเห็นบน Hacker News
  • ในที่สุดก็เปลี่ยนใจและจะออก เวอร์ชันเมเจอร์ใหม่ของ htmx
    แต่เพื่อรักษาสัญญาที่ว่า “จะไม่มี 3.0” เวอร์ชันถัดไปจึงถูกตั้งชื่อเป็น htmx 4.0
    พร้อมแซวขำ ๆ ว่าในเชิงเทคนิคก็ถือว่าเรียกได้ถูกต้อง

    • เป็นวิธีแก้ที่ขำดี แต่ก็อาจทำให้ผู้ใช้สับสนได้เหมือน PHP 6 ที่หายไปในอดีต
      เลยรู้สึกว่าสู้เรียกตรง ๆ ว่า 3.0 ไปเลยอาจจะดีกว่า
    • มีคนแซวว่าควรข้ามไปเป็น htmx 4.1 เลย แล้วทำ “xhtmx 1.0” แทน
    • บอกว่าให้อารมณ์เหมือน Leisure Suit Larry 4: The Missing Floppies
    • ยังดีที่ไม่ได้พูดว่า “จะไม่มีเวอร์ชันที่ 3” เลยยังพอมีช่องให้ตีความได้
  • มีการบอกว่า htmx 2.0 จะได้รับการซัพพอร์ตถาวร จึงไม่มีแรงกดดันให้ต้องอัปเกรดเลย
    ในยุคนี้ที่ API เปลี่ยนกันบ่อย วิธีคิดแบบนี้ถือว่าน่าชื่นชม

  • รู้สึกว่าคนที่ให้ความสำคัญกับเสถียรภาพมักได้บทเรียนที่ผิดอยู่บ่อยครั้ง
    แทนที่จะยัดการเปลี่ยนแปลงใหญ่ทั้งหมดเข้าไปครั้งเดียวแบบ Python 3.0 ก็เสนอว่า กลยุทธ์การออกเวอร์ชันแบบค่อยเป็นค่อยไป น่าจะดีกว่า
    เช่น ค่อย ๆ ใส่การเปลี่ยนแปลงผ่าน 2.1, 4.0, 5.0 แบบนี้จะจัดการง่ายกว่ามาก
    และแนะนำแนวทางแบบ Django ที่คง ช่วงความเข้ากันได้ย้อนหลัง เอาไว้ข้ามหลายเวอร์ชัน

  • รู้สึกว่าคำอธิบายของแอตทริบิวต์ hx-target ชวนสับสน
    คำว่า “inherited” ดูไม่เป็นธรรมชาติเท่า “inheritable” หรือ “inherit”

    • มีคนบอกว่าตัวเองอ่านเป็นความหมายว่า “ลูกเป็นฝ่ายสืบทอด” เลยเสนอว่าคำอย่าง “pass-down” อาจชัดเจนกว่า
    • บอกว่า “inherited” เป็นคำที่ไม่ถูกต้อง และ “inherit” ก็สั้นดีอยู่แล้ว
      พร้อมแซวว่าใช้ “bequeath” ก็ยังได้
    • ผู้เขียนบอกว่ากำลังชั่งใจระหว่างหลายคำ และพร้อมรับฟังความเห็น
    • ยังมีการเสนอทางเลือกอย่าง “heritable” หรือ “cascade” ด้วย
  • มองว่าแนวคิดของ HTMX และปรัชญาแบบ Hypermedia นั้นยอดเยี่ยม
    แต่พอออกจากโลก SPA แล้วกลับเลือก Datastar เพราะรู้สึกว่า ความสามารถในการขยายต่อ คุ้มค่ากับเวลาที่ใช้เรียนรู้มากกว่า
    มีการ push สัญญาณจากเซิร์ฟเวอร์ไปยังเบราว์เซอร์โดยตรง ทำให้ตัดโค้ด polling ทิ้งไปได้ และลดความซับซ้อนลงอย่างมาก
    ยังตัดการพึ่งพา Alpine.js ออกไปด้วย ทำให้โค้ดเรียบง่ายขึ้น และการ อัปเดตทั้งวิวแบบสตรีมมิง ก็ทำงานได้มีประสิทธิภาพเพราะการบีบอัดข้อมูล
    ถ้ามาจากฝั่ง SPA ก็แนะนำให้ลองทั้ง Datastar และ HTMX

  • การย้ายไปใช้ fetch() เพื่ออาศัย Readable Stream นั้นน่าสนใจ
    แม้จะเคยใช้ HTMX มามาก แต่ช่วงนี้กลับชอบ สตรีมมิงบน SSE ของ Datastar มากกว่า

  • ยินดีกับการพัฒนาของ HTMX แต่ก็รู้สึกว่า Datastar มี API ที่ยืดหยุ่นและเป็นมิตรกับมาตรฐานมากกว่า
    ในแพ็กเกจเล็ก ๆ มีทั้ง Fetch, SSE, declarative signals, DOM morphing และฟีเจอร์อีกหลายอย่าง
    เลยเกิดคำถามว่า “แล้วทำไมต้องใช้ HTMX?”

    • มีคนชี้ว่า Datastar Pro ไม่ได้เป็นโอเพนซอร์ส จึงมีความเสี่ยงด้านความน่าเชื่อถือ
    • มีการพูดถึง ความย้อนแย้ง ที่ผู้สร้าง Datastar เคยพยายามใส่ฟีเจอร์ลักษณะนี้เข้าไปใน HTMX มาก่อน
    • อธิบายว่าจุดแข็งของ HTMX คืออินเทอร์เฟซที่เรียบง่ายและเหมาะกับ แนวคิดแบบ request/response
      ส่วน Datastar เน้น event stream จึงเหมาะกับแดชบอร์ดเรียลไทม์หรือเกม แต่เว็บแอปส่วนใหญ่มักได้ประโยชน์จากความเรียบง่ายของ HTMX มากกว่า
      อ้างอิงที่เกี่ยวข้อง: Datastar essay, Less HTMX is More
    • บอกว่า HTMX รองรับ การจัดการ URL history ของเบราว์เซอร์ได้ง่าย แต่ Datastar กลับจำกัดฟีเจอร์นี้ไว้ในรุ่นเสียเงิน (Pro)
  • มีคนเหน็บว่าจากเดิมที่พูดว่า “สมบูรณ์แบบจนไม่ต้องมีเมเจอร์เวอร์ชันอีกแล้ว” สุดท้ายก็ยอมรับ ความจำเป็นในการพัฒนา อยู่ดี
    พร้อมยกคำพูดเรื่อง “ตอนนี้จะเปลี่ยนมาตรฐานการตั้งชื่ออีเวนต์” มาแซวว่าเป็นความพยายามหลีกเลี่ยง JavaScript

    • มีคนประชดว่า “สุดท้ายพยายามไม่ใช้ JavaScript แต่ก็ลงเอยด้วยการให้ JS มาแปล ไวยากรณ์แบบกำหนดเอง
      ถึงอย่างนั้นก็ยอมรับว่าการแสดงเจตนาใน HTML มีข้อดี
    • มีคนเล่นมุกว่า “เวอร์ชันนี้แหละสุดท้ายจริง”
    • บางคนก็มองในแง่ดีว่า “อย่างน้อยก็ดีกว่าเขียน JavaScript เองโดยตรง”
    • อีกเสียงหนึ่งบอกว่า “แล้วการที่ผู้เขียนเปลี่ยนความคิดมันมีปัญหาตรงไหน” พร้อมชี้ว่าโทนวิจารณ์ดูรุนแรงเกินไป
  • มีคนชมว่าบทความเขียนได้ชัดเจนมาก และช่วยให้เรียนรู้ ปรัชญาการออกแบบ API ได้ดี

  • มีคนสร้าง xhr-fetch-proxy เพื่อใช้ fetch ร่วมกับ HTMX
    และมองว่าการเปลี่ยนแปลงครั้งนี้จะเปิดโอกาสใหม่ ๆ ได้มากขึ้น
    ลิงก์โปรเจกต์

    • ใน 4.0 วงจรการรีเควสต์ถูกเปิดออกทั้งหมด จึงสามารถ สลับ implementation ของ fetch ได้ในแต่ละรีเควสต์
      พร้อมเสริมว่าไอเดียนี้ได้มาจาก fixi.js