10 คะแนน โดย GN⁺ 2025-10-11 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • แม้การใช้ HTMX จะช่วยลดปริมาณโค้ดได้ราว 70% แต่ก็พบปัญหาเรื่องการซิงก์ระหว่าง UI และความซับซ้อนของ การจัดการสถานะฝั่งฟรอนต์เอนด์ ที่เพิ่มขึ้น
  • หลังนำ Datastar มาใช้ การพัฒนาแอปพลิเคชันแบบเรียลไทม์สำหรับผู้ใช้หลายคนทำได้ด้วย โค้ดที่กระชับและดูแลง่ายขึ้น โดยไม่ต้องใช้ WebSockets
  • ขณะที่ HTMX กระจายตรรกะการทำงานไว้ตามแอตทริบิวต์ของ HTML Datastar กลับเพิ่ม ความสม่ำเสมอและความสามารถในการบำรุงรักษา ของตรรกะผ่าน โมเดลการอัปเดตที่ขับเคลื่อนโดยเซิร์ฟเวอร์
  • รู้สึกว่า Datastar API มีแอตทริบิวต์น้อยกว่า และช่วยเพิ่มทั้ง ความอ่านง่ายของโค้ดและผลิตภาพ
  • Datastar ใช้เทคโนโลยีเว็บเนทีฟอย่างเต็มที่ เช่น Server-Sent Events(SSE), Web Components, CSS View Transitions เพื่อให้ทำงานร่วมกันแบบเรียลไทม์และสร้างโครงสร้างคอมโพเนนต์ที่นำกลับมาใช้ซ้ำได้

บทนำและแรงจูงใจ

  • ในปี 2022 David Guillot ได้แบ่งปันกรณีศึกษาที่งาน DjangoCon Europe ว่าเขา ย้าย SaaS ที่สร้างด้วย React มาเป็น HTMX แล้วลดปริมาณโค้ดได้ประมาณ 70% พร้อมปรับปรุงฟีเจอร์
  • หลังจากนั้น หลายทีมก็พบว่าเมื่อย้ายจาก single-page app (SPA) ไปเป็น hypermedia app แบบหลายหน้า พวกเขาลดโค้ดลงได้และได้ทั้ง ประสบการณ์นักพัฒนาและประสบการณ์ผู้ใช้ ที่ดีขึ้น
  • ผู้เขียนเองก็ย้ายโปรเจกต์จาก HTMX ไปเป็น Datastar และพบว่าโค้ดสั้นลง พร้อมทั้งสามารถพัฒนา แอปเรียลไทม์แบบหลายผู้ใช้ ได้โดยไม่ต้องใช้ WebSocket หรือการจัดการสถานะที่ซับซ้อน

ปัญหาที่เป็นจุดเริ่มต้นของการย้าย

  • ระหว่างเตรียม งานบรรยาย FlaskCon 2025 ผู้เขียนพยายามซิงก์ UI โดยใช้ HTMX ร่วมกับ AlpineJS แต่กลับเจอปัญหาเรื่องการซิงก์ UI
    • ไลบรารีทั้งสองเป็นเครื่องมือแยกกันที่สร้างโดยคนละผู้พัฒนา จึง สื่อสารกันเองไม่ได้ และนักพัฒนาต้องมารับหน้าที่รวมระบบด้วยตัวเอง
    • กระบวนการตั้งค่าเริ่มต้นคอมโพเนนต์ในหลายจังหวะเวลาและการประสานอีเวนต์ ทำให้ต้องเขียนโค้ดและใช้เวลาดีบักมากกว่าที่คาด
  • ผู้เขียนจึงลองใช้ Datastar เพราะสนใจที่มันรวมความสามารถของทั้งสองไลบรารีไว้ด้วยกัน แต่มีขนาด ต่ำกว่า 11KB
    • เป็นข้อดีต่อประสิทธิภาพการโหลดหน้าเว็บสำหรับผู้ใช้บนอุปกรณ์พกพา

การออกแบบ API ของ Datastar ที่ดีกว่า

  • API ของ Datastar ให้ความรู้สึก เบากว่า HTMX มาก และต้องเพิ่มแอตทริบิวต์น้อยกว่าเพื่อให้ได้ผลลัพธ์ที่ต้องการ
  • HTMX ต้องใช้หลายแอตทริบิวต์ในปฏิสัมพันธ์ส่วนใหญ่
    • ตั้งค่า URL, ระบุ target element, และกำหนดวิธีจัดการ response แยกกันคนละแอตทริบิวต์
    • โดยทั่วไปต้องใช้ 2–3 แอตทริบิวต์ แทบทุกครั้ง และบางครั้งต้องไล่ดูตามสาย inheritance เพื่อเข้าใจว่าแอตทริบิวต์ทำงานอย่างไร
    <a hx-target="#rebuild-bundle-status-button"  
       hx-select="#rebuild-bundle-status-button"  
       hx-swap="outerHTML"  
       hx-trigger="click"  
       hx-get="/rebuild/status-button"></a>  
    
  • Datastar โดยทั่วไปใช้ เพียงแอตทริบิวต์เดียว เพื่อทำงานแบบเดียวกัน
    <a data-on-click="@get('/rebuild/status-button')"></a>  
    
    • ต่อให้กลับมาอ่านโค้ดอีกครั้งในอีกไม่กี่เดือน ก็ยังเข้าใจวิธีทำงานได้ง่าย

ความต่างของหลักการทำงาน

  • HTMX เป็นไลบรารีฝั่งฟรอนต์เอนด์ ที่มุ่งขยายสเปก HTML ขณะที่ Datastar เป็นไลบรารีแบบเซิร์ฟเวอร์เป็นผู้ขับเคลื่อน ที่มุ่งสร้างแอปอัปเดตเรียลไทม์ประสิทธิภาพสูงด้วยเทคโนโลยีเว็บเนทีฟ
  • HTMX นิยามพฤติกรรมผ่านการเพิ่มแอตทริบิวต์ลงบนองค์ประกอบที่ใช้ทริกเกอร์ request และแม้จะอัปเดตองค์ประกอบที่อยู่ไกลออกไปในหน้าเดียวกัน ตรรกะก็ยังถูกกระจายอยู่หลายชั้น
  • Datastar ให้ เซิร์ฟเวอร์ตัดสินใจว่าจะเปลี่ยนอะไร ทำให้รวมตรรกะการอัปเดตทั้งหมดไว้ในที่เดียว
  • ตัวอย่าง HTMX

    <div>  
      <div id="alert"></div>  
        <button hx-get="/info"   
                hx-select="#info-details"   
                hx-swap="outerHTML"  
                hx-select-oob="#alert">  
            Get Info!  
        </button>  
    </div>  
    
    • เมื่อกดปุ่ม จะส่ง GET request ไปที่ /info แล้วแทนที่ปุ่มด้วยองค์ประกอบที่มี ID เป็น info-details จาก response และแทนที่องค์ประกอบในหน้าที่มี ID เป็น alert ด้วยองค์ประกอบ alert จาก response
    • ปุ่มต้องรับรู้ข้อมูลมากเกินไป และยังต้องรู้ล่วงหน้าว่าเซิร์ฟเวอร์จะส่งอะไรกลับมา จึงทำให้หลักการ "locality of behavior" ของ HTMX อ่อนลง
  • แนวทางที่ปรับปรุงแล้วของ Datastar

    <div>  
        <div id="alert"></div>  
        <button id="info-details"  
        data-on-click="@get('/info')">  
            Get Info!  
        </button>  
    </div>  
    
    • เซิร์ฟเวอร์ส่งกลับสตริง HTML ที่มี root element สองตัวซึ่งใช้ ID เดียวกันกับของเดิม
      <p id="info-details">These are the details you are looking for…</p>  
      <div id="alert">Alert! This is a test.</div>  
      
    • เป็นตัวเลือกที่เรียบง่ายและมีประสิทธิภาพดี

คิดในระดับคอมโพเนนต์

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

    • สถานะ placeholder:
      <!-- info-component-placeholder.html -->  
      <div id="info-component">  
          <button data-on-click="@get('/product/{{product.id}}/info')">  
              Get Info!  
          </button>  
      </div>  
      
    • สถานะแสดงข้อมูล:
      <!-- info-component-get.html -->  
      <div id="info-component">  
          {% if alert %}<div id="alert">{{ alert }}</div>{% endif %}  
          <p>{{product.additional_information}}</p>  
      </div>  
      
    • เมื่อเซิร์ฟเวอร์เรนเดอร์ HTML แล้ว Datastar จะอัปเดตหน้าให้อัตโนมัติ
    • การคิดในระดับคอมโพเนนต์ช่วย ป้องกันการเข้าสู่สถานะที่ผิดหรือทำสถานะของผู้ใช้หายไป

อัปเดตหลายคอมโพเนนต์พร้อมกัน

  • จุดที่น่าประทับใจจากงานบรรยายของ David Guillot คือ เมื่อแอปอัปเดตจำนวนรายการโปรด มันยังอัปเดตองค์ประกอบตัวนับที่อยู่ไกลจากคอมโพเนนต์ต้นทางมากได้พร้อมกันด้วย
    • HTMX ทำได้โดยทริกเกอร์ JavaScript event แล้วให้อีเวนต์นั้นไปทริกเกอร์ GET request กับคอมโพเนนต์ที่อยู่ไกลออกไปอีกที
  • Datastar สามารถ อัปเดตหลายคอมโพเนนต์พร้อมกันได้แม้อยู่ในฟังก์ชัน synchronous
  • ตัวอย่างตะกร้าสินค้า

    • คอมโพเนนต์เพิ่มสินค้าลงตะกร้า:
      <form id="purchase-item"  
            data-on-submit="@post('/add-item', {contentType: 'form'})">"  
      >  
        <input type=hidden name="cart-id" value="{{cart.id}}">  
        <input type=hidden name="item-id" value="{{item.id}}">  
        <fieldset>  
          <button data-on-click="$quantity -= 1">-</button>  
          <label>Quantity  
            <input name=quantity type=number data-bind-quantity value=1>  
          </label>  
          <button data-on-click="$quantity += 1">+</button>  
        </fieldset>  
        <button type=submit>Add to cart</button>  
        {% if msg %}  
          <p class=message>{{msg}}</p>  
        {% endif %}  
      </form>  
      
    • คอมโพเนนต์แสดงจำนวนในตะกร้า:
      <div id="cart-count">  
          <svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">  
              <use href="#shoppingCart">  
          </svg>  
          {{count}}  
      </div>  
      
    • ใน Django สามารถอัปเดตทั้งสองคอมโพเนนต์ได้ด้วย request เดียวกัน:
      from datastar_py.consts import ElementPatchMode  
      from datastar_py.django import (  
          DatastarResponse,  
          ServerSentEventGenerator as SSE,  
      )  
      
      def add_item(request):  
          # omitted important state updates  
          return DatastarResponse([  
              SSE.patch_elements(  
                  render_to_string('purchase-item.html', context=dict(cart=cart, item=item, msg='Item added!'))  
              ),  
              SSE.patch_elements(  
                  render_to_string('cart-count.html', context=dict(count=item_count))  
              ),  
          ])  
      

ปรัชญาแบบเว็บเนทีฟ

  • จากชุมชน Datastar บน Discord ผู้เขียนเข้าใจว่า Datastar ไม่ใช่แค่ helper script ธรรมดา แต่คือ แนวคิดในการสร้างแอปด้วย primitive พื้นฐานของเว็บ
  • ขณะที่ HTMX พยายามผลักดันสเปก HTML ไปข้างหน้า Datastar กลับสนใจ การส่งเสริมการใช้ความสามารถเว็บเนทีฟ มากกว่า
    • CSS view transitions
    • Server-Sent Events
    • Web Components เป็นต้น
  • ผู้เขียนประสบความสำเร็จอย่างมากจากการรีแฟกเตอร์คอมโพเนนต์ AlpineJS ที่ซับซ้อนให้กลายเป็น Web Components แบบเรียบง่าย แล้วนำกลับมาใช้ซ้ำในหลายจุด
  • นี่เป็นแพตเทิร์นที่ยอดเยี่ยมสำหรับการสร้าง custom HTML element ที่มี locality of behavior สูงและนำกลับมาใช้ซ้ำได้ โดยไม่ต้องพึ่งเครื่องมืออย่าง React

การอัปเดตแบบเรียลไทม์สำหรับแอปหลายผู้ใช้

  • แอปที่มีความสามารถด้านการทำงานร่วมกันเป็นฟีเจอร์ระดับแรกเริ่มย่อมสร้างความแตกต่างจากแอปอื่น และ Datastar ก็ช่วยแก้โจทย์นี้ได้
  • นักพัฒนา HTMX ส่วนใหญ่มัก polling ดึงข้อมูลจากเซิร์ฟเวอร์ หรือไม่ก็เขียน โค้ด WebSocket แบบคัสตอม ซึ่งเพิ่มความซับซ้อน
  • Datastar ใช้เทคโนโลยีเว็บที่เรียบง่ายอย่าง Server-Sent Events(SSE) ให้เซิร์ฟเวอร์ “push” การอัปเดตไปยังไคลเอนต์ที่เชื่อมต่ออยู่
    • เมื่อผู้ใช้เพิ่มคอมเมนต์หรือมีการเปลี่ยนสถานะ เซิร์ฟเวอร์จะอัปเดตเบราว์เซอร์ทันที โดยต้องเพิ่มโค้ดเพียงเล็กน้อย
    • สามารถสร้างแดชบอร์ดแบบเรียลไทม์ แผงแอดมิน และเครื่องมือทำงานร่วมกันได้โดยไม่ต้องเขียน JavaScript แบบคัสตอม
  • หากการเชื่อมต่อฝั่งไคลเอนต์หลุด เบราว์เซอร์จะ พยายามเชื่อมต่อใหม่ให้อัตโนมัติ โดยไม่ต้องมีโค้ดเพิ่มเติม
    • และยังสามารถแจ้งเซิร์ฟเวอร์ได้ด้วยว่า “อีเวนต์ล่าสุดที่ได้รับ” คืออะไร

หลีกเลี่ยงความซับซ้อนเกินจำเป็น

  • ชุมชน Datastar บน Discord ช่วยให้เข้าใจวิสัยทัศน์ของ Datastar ต่อการสร้างเว็บแอป
    • การอัปเดต UI แบบ push-based
    • การลดความซับซ้อน
    • การใช้เครื่องมืออย่าง Web Components เพื่อจัดการกรณีซับซ้อนเฉพาะจุด
  • ชุมชนยังช่วยให้ผู้ใช้ใหม่ตระหนักว่าตนเองกำลังเข้าหาปัญหาอย่างซับซ้อนเกินไป

เคล็ดลับสำคัญ

  • อย่ากลัวที่จะ เรนเดอร์ทั้งคอมโพเนนต์ใหม่ทั้งหมด แล้วส่งออกไป
    • มันง่ายกว่าและแทบไม่กระทบประสิทธิภาพมากนัก
    • ยังได้อัตราการบีบอัดที่ดีกว่า และเบราว์เซอร์ก็ parse สตริง HTML ได้เร็วมาก
  • เซิร์ฟเวอร์คือแหล่งความจริงของสถานะ และทรงพลังมากกว่าเบราว์เซอร์
    • ควรให้เซิร์ฟเวอร์จัดการสถานะส่วนใหญ่ และอาจไม่จำเป็นต้องใช้ reactive signals มากเท่าที่คิด
  • Web Components เหมาะมากสำหรับการห่อหุ้มตรรกะไว้ใน custom element ที่มี locality of behavior สูง
    • แอนิเมชันสนามดาวในส่วนหัวของ เว็บไซต์ Datastar เป็นตัวอย่างที่ดี
    • องค์ประกอบ <ds-starfield> ห่อหุ้มโค้ดทั้งหมดของแอนิเมชันสนามดาวไว้ และเปิดเผยพร็อพเพอร์ตี 3 ตัวสำหรับเปลี่ยนสถานะภายใน
    • Datastar จะขับเคลื่อนพร็อพเพอร์ตีเหล่านั้นเมื่อค่าจาก range input เปลี่ยน หรือเมื่อเมาส์เคลื่อนผ่านองค์ประกอบ

ศักยภาพที่ก้าวข้ามข้อจำกัด

  • สิ่งที่น่าตื่นเต้นที่สุดคือศักยภาพที่ Datastar เปิดให้เป็นไปได้
  • ชุมชนสร้างโปรเจกต์ที่ก้าวข้ามข้อจำกัดที่นักพัฒนาซึ่งใช้เครื่องมืออื่นมักพบเจออยู่เป็นประจำ

กรณีที่น่าจับตา

  • เดโมมอนิเตอร์ฐานข้อมูล ในหน้าตัวอย่าง
    • ใช้ Hypermedia เพื่อปรับปรุงทั้งความเร็วและการใช้หน่วยความจำอย่างมาก เมื่อเทียบกับเดโมที่เคยนำเสนอในงานประชุม JavaScript
  • 1 พันล้านเช็กบ็อกซ์ ของ Anders Murphy
    • หลังจากการทดลอง 1 ล้านเช็กบ็อกซ์เกินขีดความสามารถของเซิร์ฟเวอร์ เขาจึงใช้ Datastar เพื่อทำให้ได้ถึง 1 พันล้านบนเซิร์ฟเวอร์ราคาถูก
  • เว็บแอปที่แสดงข้อมูลจากสถานีเรดาร์ทั้งหมดในสหรัฐอเมริกา
    • เมื่อสัญญาณของเรดาร์เปลี่ยน จุดที่เกี่ยวข้องใน UI จะเปลี่ยนตามภายใน 100 มิลลิวินาที
    • มีการอัปเดต มากกว่า 800,000 จุดต่อวินาที และผู้ใช้สามารถ scrub ย้อนหลังได้ไกลสุด 1 ชั่วโมงด้วยดีเลย์ ต่ำกว่า 700 มิลลิวินาที
    • การที่สิ่งนี้เป็นไปได้ในฐานะแอป Hypermedia แสดงให้เห็นถึงสิ่งที่ Datastar ทำให้เกิดขึ้นได้

ประสบการณ์การใช้งานในปัจจุบัน

  • ผู้เขียนยังอยู่ในช่วงสำรวจ Datastar และพบว่าสามารถทำ AJAX สำหรับอัปเดต UI แบบมาตรฐานที่ HTMX ทำได้อย่างรวดเร็วและง่ายดาย
  • กำลังเรียนรู้และทดลองหลายแพตเทิร์นเพื่อใช้ Datastar ให้ทำได้มากขึ้น
  • ผู้เขียนสนใจมานานหลายสิบปีว่าทำอย่างไรจึงจะมอบประสบการณ์ผู้ใช้ที่ดีขึ้นด้วยการอัปเดตแบบเรียลไทม์ และชอบที่ Datastar ทำให้ การอัปเดตแบบ push-based เกิดขึ้นได้แม้อยู่ในโค้ด synchronous
  • ตอนเริ่มใช้ HTMX ผู้เขียนรู้สึกตื่นเต้นมาก แต่หลังจากย้ายมา Datastar ก็รู้สึกว่าไม่ได้สูญเสียอะไรไปเลย กลับกันคือ ได้อะไรมากขึ้นกว่ามาก
  • หากคุณเคยรู้สึกสนุกกับ HTMX คุณก็น่าจะได้สัมผัสความก้าวกระโดดแบบเดียวกันอีกครั้งกับ Datastar และมันให้ความรู้สึกเหมือน ค้นพบว่าเว็บควรทำอะไรได้ตั้งแต่แรก

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

 
GN⁺ 2025-10-11
ความเห็นจาก Hacker News
  • รู้สึกขอบคุณที่ Chris กล้าออกจากพื้นที่ปลอดภัยของตัวเองมาลองสิ่งใหม่ และแบ่งปันประสบการณ์นั้นกับพวกเรา ฉันเองทำเว็บแอปด้วย htmx มา 4 ปีแล้วก็เลยอาจมีอคติเล็กน้อย แต่คิดว่ามันสะท้อนความต่างด้านสถาปัตยกรรมหลักระหว่าง Datastar กับ htmx ได้ชัดเจน: htmx เป็นแบบขับเคลื่อนด้วย HTML ส่วน Datastar เป็นแบบขับเคลื่อนด้วยเซิร์ฟเวอร์ จริงอยู่ว่า API ฝั่งไคลเอนต์นั้นเรียบง่ายกว่า แต่ก็เพราะตรรกะฝั่งเซิร์ฟเวอร์ซับซ้อนขึ้น เช่น ถ้า HTML element ไม่มีข้อมูลว่าจะต้องเอา fragment ที่เซิร์ฟเวอร์ตอบกลับมาไปวางไว้ตรงไหน ข้อมูลนั้นก็ต้องถูกเก็บไว้ฝั่งเซิร์ฟเวอร์ ดังนั้นความซับซ้อนต้องไปอยู่สักฝั่งหนึ่งเสมอ การเลือกสถาปัตยกรรมก็น่าจะเป็นเรื่องของรสนิยม ส่วนตรรกะเรื่อง “less attributes” ในตัวอย่างก็ไม่ค่อยยุติธรรม 100% เพราะยกเอา attribute ที่ใน htmx ใส่หรือไม่ใส่ก็ได้มาเป็นตัวอย่างด้วย เช่น ถ้าตัด hx-trigger="click" ออก จำนวน attribute ก็ลดลง 20% แล้ว และถ้าเขียน HTML ให้เข้าถึงได้ดีกว่านี้ เช่น ใช้ <span> แทน <button> ก็น่าจะทำให้ตัวอย่างน่าเชื่อถือขึ้น สุดท้ายแล้วจุดแข็งของ Datastar ดูเหมือนจะเป็นการมีความสามารถแบบ Alpine หรือ Stimulus ติดมาในตัว ซึ่งน่าประทับใจมาก
    • ฉันคิดว่าถ้าใช้ Datastar ความซับซ้อนอาจลดลงมาก เพราะไม่ต้องไปทำระบบ eventing แยกต่างหากเพื่ออัปเดตส่วนอื่นของหน้าแบบเรียลไทม์ แต่สามารถดึงลงมาทีเดียวแล้วอัปเดตทั้งหมดได้ แน่นอนว่าในบางกรณี วิธีแบบ event-based หรือการโหลดทีหลังก็อาจเหมาะกว่า
    • พอเห็นความเห็นที่ว่า “เหมือนเอาฟีเจอร์ของ Alpine หรือ Stimulus มาใส่ไว้ใน HTMX ตั้งแต่ต้น” ก็เริ่มลังเลว่าจะใช้ HTMX กับโปรเจกต์ส่วนตัวดีไหม เลยสงสัยว่ามีเอกสารไหนที่อธิบายไหมว่าจำเป็นต้องใช้ไลบรารีเสริมอย่าง AlpineJS หรือ Stimulus ด้วยหรือเปล่า
    • มีการคุยกันว่าถ้า HTML element ไม่มีข้อมูลว่าจะแทรก fragment ตรงไหน เซิร์ฟเวอร์ก็ต้องรู้แทน แต่ฉันก็คิดว่าฝั่งฟรอนต์เอนด์น่าจะเบาและเร็วกว่าในกรณีนี้หรือเปล่า โดยเฉพาะถ้ามี element จำนวนมากยิ่งน่าจะใช่ไหม
    • โครงสร้างนี้คล้ายกับเฟรมเวิร์ก Seaside ของ Pharo อยู่เหมือนกัน ตอนที่บริษัทเราทำแอป B2B ด้วย Pharo เราจัดการ UI state ไว้ที่แบ็กเอนด์ เลยมีการไปมาระหว่างฟรอนต์กับแบ็กเอนด์เยอะ ถ้าเป็นงาน B2B ที่เรื่องเรียลไทม์หรือ latency ไม่สำคัญมากก็โอเค แต่กับแอป B2C ที่ต้องสเกลสูงคงไม่เหมาะ
  • จากมุมคนที่เคยใช้ทั้ง Datastar และ HTMX โดยตรง ฉันยังไม่ค่อยเห็นว่าการเขียนแอปด้วย Datastar จะต่างกันมากแค่ไหน ตอนนี้ใช้ FastAPI, HTMX, Alpine.js และ SSE ร่วมกันเพื่อทำพวกแสดง log แบบเรียลไทม์ อัปเดตสถานะการ deploy อะไรทำนองนี้ พอดูตัวอย่าง Datastar แล้วก็ยังไม่เห็นชัดว่ามันง่ายกว่าโครงสร้างนี้ตรงไหน (ดูโค้ดได้ที่: devpush SSE partial) เคยลอง Web Components ตอนพัฒนา Basecoat เหมือนกัน แต่สุดท้ายก็กลับไปใช้ HTML/CSS/JS แบบดั้งเดิมเพราะปัญหาเรื่องสไตล์ การจัดการ state และเหตุผลอีกหลายอย่าง devpu.sh, basecoatui.com
    • แม้แต่ใน HTMX เอง เวลาคิดเรื่องความยืดหยุ่นเชิงฟังก์ชันแบบ Datastar หลายครั้งการอัปเดตรายการทั้งก้อนทีเดียวกลับง่ายกว่า ถ้าอัปเดตทั้งรายการแทนที่จะคอยอัปเดตสถานะการ deploy ทีละตัว ก็ไม่ต้องกังวล edge case อย่าง pagination ทำให้โค้ดง่ายและเบากว่ามาก
  • ถ้าใครคิดว่า Datastar ยังไม่พอสำหรับงานเรียลไทม์/ทำงานร่วมกัน/มัลติเพลเยอร์ อยากแนะนำเดโม 3 ตัวที่ทำงานได้โดยไม่ต้องใช้ฟีเจอร์ PRO และถึงขั้นขึ้นหน้าแรก HN บน VPS ราคา 5 ดอลลาร์ด้วย เดโมเหล่านี้แสดงให้เห็นว่า Datastar เป็นเทคโนโลยีที่ทำมาดีแค่ไหน: Checkboxes, Cells, Game of Life Example. ตัวอย่าง checkbox กับ cells มีการเรนเดอร์ view แบบยืดหยุ่นมากจนซูมออกได้ค่อนข้างไกล และยังมี backpressure สำหรับ virtual scrolling ด้วย
    • ถ้าฉันเข้าใจโครงสร้างโค้ดถูก จริง ๆ แล้วดูเหมือนว่าเขาไม่ได้ใช้แนวทาง patch แบบ diff/patch ที่ datastar แนะนำ แต่เรนเดอร์ทั้งหน้าใหม่ทุกครั้งแทน ซึ่งจริง ๆ mental model แบบนี้ดูง่ายกว่าตัวอย่างที่ต้องติดตาม state ฝั่งไคลเอนต์อย่างละเอียดเสียอีก เลยกลับรู้สึกดึงดูดขึ้นมา อยากรู้ว่าแอปทั่วไปที่ซับซ้อนกว่านี้ก็สร้างแบบนี้ได้ไหม ถ้าผู้ใช้ย้ายไปคนละหน้าแล้วต้องติดตาม state ของ widget หลายแบบเพื่อเรนเดอร์ใหม่ได้ทันที มีบทความหรือเอกสารไหนที่น่าอ่านบ้างก็อยากให้แนะนำ
    • ที่บอกว่าตัวอย่าง checkbox/cells “ซูมออก” ได้ ฉันอยากรู้ว่าทำยังไงแบบละเอียด และถ้ามีตัวเลือกอย่าง data-replace-url ที่อัปเดต URL ของ view ปัจจุบันอัตโนมัติตามพิกัดนั้น ๆ (เช่น x=123&y=456) ก็น่าจะดีมาก
    • พอเห็นการพูดถึงฟีเจอร์ PRO ก็เลยเพิ่งรู้ว่ามันเป็นโมเดล open-core (บางส่วนโอเพนซอร์ส ที่เหลือเสียเงิน และไลเซนส์ 299 ดอลลาร์) แบบนี้ฉันขอผ่านดีกว่า
  • ไม่นานมานี้ฉันเพิ่งอ่านโพสต์นี้ (htmx, datastar, greedy developer) และได้ยินมาว่าฟีเจอร์แกนหลักดี ๆ ของ Datastar ถูกย้ายไปอยู่ฝั่งเสียเงิน (Pro) แล้ว ไม่ว่าจะโอเพนซอร์สหรือเสียเงิน ฉันก็อยากสนับสนุนเฟรมเวิร์กที่ตัวเองใช้ในเชิงการเงินนะ แต่แบบอย่างลักษณะนี้ก็น่ากังวลอยู่
    • ฉันเองก็ติดตาม Datastar มาหลายเดือน รอการออก 1.0.0 อยู่เหมือนกัน แต่ตอนนี้ความคาดหวังหายไปหมดแล้ว เจอกรณี “โอเพนซอร์สแต่จริง ๆ ไม่ใช่” แบบนี้บ่อยจนเชื่อใจไม่ค่อยลง
    • จริง ๆ ปกติฉันออกแนวไม่ค่อยชอบ Datastar ด้วยซ้ำ แต่ครั้งนี้กลับอยากออกมาปกป้องมันหน่อย ผู้สร้างเฟรมเวิร์กเอาโค้ดของตัวเองออกให้ใช้ฟรีภายใต้ MIT license ดังนั้นฟีเจอร์ที่เคยปล่อยฟรีในอดีตก็ยังใช้ต่อภายใต้ MIT ได้เหมือนเดิม ในฐานะคนที่ใช้อย่างเดียวโดยไม่ได้มีส่วนร่วม จะเลือกพึ่งพาเวอร์ชันเก่าก็เป็นสิทธิของตัวเอง และจากนี้ไปเจ้าของผลิตภัณฑ์จะเปลี่ยนไปใช้โมเดลเสียเงินก็เป็นสิทธิของเขาเหมือนกัน ถ้าจำเป็นก็แค่ fork เอา
    • ฉันจ่ายซื้อ PRO license 299 ดอลลาร์แบบครั้งเดียวไปแล้ว แต่จนถึงตอนนี้ก็ยังไม่เคยใช้ฟีเจอร์ PRO จริง ๆ เลย ตั้งใจจะทำ Google Sheets clone แต่สุดท้ายก็ทำได้สบายโดยไม่ต้องใช้ PRO (ดูเดโม cells)
    • ฝั่ง Datastar บางทีก็ดูเหมือนโปรโมตว่ามันดีแค่ไหนอยู่เรื่อย ๆ ใน Discord ของ HTMX จนรู้สึกเกือบจะก้าวร้าวนิด ๆ ด้วย เคยเห็นคอมเมนต์ใน reddit ประมาณว่า “ถ้ามีฟีเจอร์ที่ต้องการครบแล้วก็ใช้เวอร์ชัน beta ไปตลอดสิ โอเพนซอร์สไม่ได้ติดหนี้ใคร”
    • ใช้ Datastar แล้วนึกถึงกรณี Meteor.js ในอดีตขึ้นมาทันที (HN discussion ของ Meteor.js)
  • ฉันไม่ค่อยเข้าใจโค้ดตัวอย่างในโพสต์นี้ เช่น ตัวอย่าง htmx ข้างล่าง <span hx-target="#rebuild-bundle-status-button" hx-select="#rebuild-bundle-status-button" hx-swap="outerHTML" hx-trigger="click" hx-get="/rebuild/status-button"></span> ดูเหมือนจะถูกเปลี่ยนเป็นโค้ด datastar แบบนี้: <span data-on-click="@get('/rebuild/status-button')"></span> และพอไปดูตัวอย่างอื่น ๆ ก็ยิ่งสับสน สรุปแล้วไม่เข้าใจจริง ๆ ว่าทำไมถึงย้ายจาก htmx ไป Datastar
    • โดยพื้นฐานแล้ว HTMX หมายถึง “เมื่อคลิก span นี้ ให้ดึง HTML จาก /rebuild/status-button จากนั้นดึง element #rebuild-bundle-status-button จาก HTML ที่ตอบกลับมา แล้วแทนที่ element เดิม” ส่วน Datastar คือ “เมื่อคลิก span ให้ทำตามคำสั่งจาก /rebuild/status-button ตรง ๆ” ถ้าเซิร์ฟเวอร์คืน element ที่มี ID หลายตัวมา Datastar จะรู้เองว่าต้องจับคู่แล้วแทนที่ element เหล่านั้นทั้งหมด ดังนั้นไม่ต้องใช้ target, select, swap ครบชุด แค่มี ID ก็ทำงานตามที่ตั้งใจได้
    • โครงสร้างของ Datastar คือการย้ายตรรกะไปกองไว้ที่แบ็กเอนด์ เหมือน HTML แบบดั้งเดิมสมัยก่อนที่ขอแล้วได้ HTML กลับมาให้เบราว์เซอร์เรนเดอร์ทันที แต่ Datastar ทำแบบ PWA มากกว่า คือพอโหลดหน้ามาครั้งหนึ่งแล้ว ทุกครั้งที่มีการโต้ตอบก็จะส่งคำขอไปที่แบ็กเอนด์ แล้วรับเฉพาะส่วนที่เปลี่ยนกลับมาสะท้อนผล เป็นโครงสร้างตรงข้ามกับ SPA ที่ไม่มีตรรกะฝั่งฟรอนต์และให้แบ็กเอนด์จัดการ state ทั้งหมด โดยแก่นแล้วมันคือคำถามเดิมเรื่องการแบ่งตรรกะระหว่างแบ็กเอนด์/ฟรอนต์เอนด์ แต่ Datastar ทำให้สามารถกองตรรกะไว้ที่เซิร์ฟเวอร์โดยยังคงอินเทอร์เฟซแบบไดนามิกได้
    • แต่ก็ยังสงสัยอยู่ดีว่าทำไมถึงใช้แท็ก span สำหรับการคลิก ปุ่มหรือแท็กลิงก์น่าจะเหมาะกว่านะ
  • น่าขันดีที่บทความนี้พูดถึง hypermedia แต่กลับไม่มีลิงก์เว็บไซต์ทางการของ Datastar เลย นี่คือลิงก์: https://data-star.dev/
  • ข้อดีอย่างหนึ่งของ HTMX คือฝั่งไคลเอนต์ไม่จำเป็นต้องรู้โครงสร้างของข้อมูลที่เซิร์ฟเวอร์ส่งกลับมา แต่ถ้าฝั่งไคลเอนต์ต้องรู้ถึงระดับ ID หรือความหมายของแต่ละ element ก็เหมือนคำสัญญานี้จะถูกทำลาย แน่นอนว่าในโปรเจกต์จริงหลายแห่งก็ใช้ OOB (Out of Band) กันมากอยู่แล้ว จึงอาจพูดได้ว่าการแยกโครงสร้างอย่างสมบูรณ์นั้นก็ห่างจากความเป็นจริงพอสมควร หวังว่าจะมีวิธีที่เอาข้อดีของทั้งสองโลกมารวมกันได้
    • ในทางปฏิบัติ ฝั่งไคลเอนต์อาจไม่ต้องรู้อะไรเลยก็ได้ ถ้าฝั่งไคลเอนต์สั่ง action แล้วเซิร์ฟเวอร์ส่ง view ของทั้งหน้ากลับมาให้ ไคลเอนต์ก็เรนเดอร์ทั้งหมดทันที คล้ายกับโมเดล immediate mode ของวิดีโอเกม
  • โครงสร้างแบบที่ Datastar ให้เซิร์ฟเวอร์มาคอย patch HTML อย่างนี้ ดูไม่ค่อยดีในแง่ separation of concerns และยิ่งถ้าเป็นแอปขนาดใหญ่ การคอยฉีดส่วน HTML จากเซิร์ฟเวอร์ก็น่าจะจัดการลำบากมาก
    • แต่ถ้าจะให้ JS ไปฉีด HTML fragment จากหลายที่แทน มันก็คงไม่ได้ดีกว่าเท่าไร
    • วิธีออกแบบ endpoint ที่คอยปล่อยชิ้นส่วน HTML จากเซิร์ฟเวอร์แบบนี้ให้ความรู้สึกแปลก ๆ อยู่เหมือนกัน
  • Datastar ให้ความรู้สึกสมบูรณ์กว่า htmx ฉันทำโปรเจกต์สำเร็จด้วย htmx มาหลายตัวแล้ว แต่ก็เสียดายเสมอที่เรื่องจัดการ event ต้องเขียน JS glue code เพิ่มเองอีก โดยเฉพาะร่วมกับ AlpineJS ถ้า Datastar ลดความจำเป็นพวกนี้ลงได้ก็น่าตื่นเต้นมาก
    • แนะนำให้อ่านบทความ grugs around the fire ในเว็บไซต์ Datastar
  • ฉันเข้ากระแส hypermedia ค่อนข้างช้า ตอนแรกใช้ Datastar ก่อน แต่ช่วงนี้ย้ายมา HTMX แล้ว แม้ว่า API ของ Datastar จะดูดีกว่านิดหน่อย แต่ตั้งแต่ htmx 2.0 รองรับ OOB (Out-Of-Band) updates แล้ว ส่วนใหญ่ฉันก็ให้คะแนน htmx มากกว่า
    • ประโยคที่ว่า “เข้ากระแส hypermedia ช้า” น่าสนใจดี เพราะ Ted Nelson เป็นคนใช้คำนี้ครั้งแรกในปี 1965 และตอนนั้นเขาเขียนไว้ว่า “hyperfilm— a browsable or vari-sequenced movie— is only one of the possible hypermedia that require our attention” ซึ่งก็น่าสนใจดีถ้าดูในบริบทนี้ ดูบทความต้นฉบับ
    • สิ่งที่น่าเสียดายใน HTMX เวลาจัดการ element แบบ OOB คือ 1. ถ้าเป็น OOB ต้องมี htmx-swap-oob="true" เสมอ ไม่งั้นการทำงานจะไม่เป็นอย่างที่คาด 2. ในทางกลับกัน ถ้าไม่ใช่ OOB แต่มี htmx-swap-oob="true" อยู่ มันจะถูกมองข้ามหรือทำงานผิดพลาด ทำให้เวลาจะนำคอมโพเนนต์เดียวกันไปใช้ซ้ำได้ทั้งแบบ OOB/ไม่ OOB ฝั่งเซิร์ฟเวอร์ต้องส่งแฟลก isOob มาทุกครั้ง ซึ่งยุ่งยากมาก
    • ฉันชอบ API ของ alpine-ajax มากกว่า แค่กำหนดหลาย target ก็สามารถแทนที่แต่ละ element ได้อย่างสม่ำเสมอโดยไม่ต้องพึ่ง JS ส่วนแนวคิด signal/state ของ Datastar กลับทำให้ความซับซ้อนเพิ่มขึ้น เลยไม่ค่อยชอบ