การแสดงผลข้อมูลประเภทกราฟต่างๆ ในหน้า dashboard ถ้ามีจำนวนไม่กี่กราฟ Query ที่ใช้ก็คงมีไม่กี่ชุด การรอดึงข้อมูลให้ครบก่อนแล้วจึงจะนำไปแสดงผลอาจจะไม่ทำให้ UX ของผู้ใช้งานรู้สึกแย่สักเท่าไหร่ เพราะคงใช้เวลาไม่กี่วินาที แต่เมื่อไหร่ก็ตามที่จำนวนกราฟมากขึ้น เงื่อนไขที่ใช้มากขึ้น Query ที่ใช้ก็ย่อมมีหลายชุดตามไปด้วย ทั้งนี้ย่อมส่งผลให้เวลาใช้ก็มากขึ้นตาม ถ้าจะรอให้ดึงข้อมูลครบทั้งหมด ผู้ใช้งานคงรอเป็นนาทีๆ เลยทีเดียว
จากตัวอย่างข้างต้น ผู้ใช้งานต้องการดึงข้อมูลหลายเดือน มีการเลือกที่จะแสดงตัวชี้วัดหลายตัวทีเดียว และผลตอบรับที่ได้กลับมาคือผู้ใช้งานต้องใช้เวลารอนานมากๆๆๆๆๆ เพราะปัจจุบันการดึงข้อมูลตรงนี้จะรอให้ดึงข้อมูลทุกๆ กราฟให้เสร็จเรียบร้อยก่อน และส่งผลกลับมาแสดงบนให้ผู้ใช้งานได้เห็นหรือที่เรียกว่า Eager Loading
จุดนี้เองทำให้เราจะต้องปรับเปลี่ยนการแสดงข้อมูลเป็น Lazy Loading แทนซึ่งน่าจะช่วยให้ UX ของผู้ใช้งานดีขึ้น ในบทความนี้เราจะมาลอง POC กันก่อนโดยมีแนวคิดที่ว่าทันทีที่ส่วนของการแสดงกราฟปรากฏขึ้นบน viewport จึงจะค่อยไปดึงข้อมูล และนำกราฟมาแสดง ซึ่งเราจะใช้ Turbo และ Stimulus
จุดนี้เองทำให้เราจะต้องปรับเปลี่ยนการแสดงข้อมูลเป็น Lazy Loading แทนซึ่งน่าจะช่วยให้ UX ของผู้ใช้งานดีขึ้น ในบทความนี้เราจะมาลอง POC กันก่อนโดยมีแนวคิดที่ว่าทันทีที่ส่วนของการแสดงกราฟปรากฏขึ้นบน viewport จึงจะค่อยไปดึงข้อมูล และนำกราฟมาแสดง ซึ่งเราจะใช้ Turbo และ Stimulus
- สร้าง controller เพื่อใช้ตรวจสอบว่าส่วนแสดงผลปรากฏขึ้นมาแล้วหรือยังด้วย Intersection Observer และทันทีที่ปรากฏก็สั่งให้ Turbo Frame ทำการโหลดข้อมูลจาก src ที่ได้กำหนดไว้
# hyper_controller.js import { Controller } from "stimulus" # AppearanceObserver from https://github.com/hotwired/turbo-rails/blob/ba86832f7f13001793ab917185788df9723666e8/app/assets/javascripts/turbo.js#L436 import { AppearanceObserver } from "../helpers" export default class extends Controller { static values = { src: String } initialize () { this.connected = false this.appearanceObserver = new AppearanceObserver(this, this.element) } connect () { if (!this.connected) { this.connected = true this.appearanceObserver.start() } } disconnect () { if (this.connected) { this.appearanceObserver.stop() } } elementAppearedInViewport(element) { this.loadSourceURL() } loadSourceURL() { this.element.src = this.srcValue this.appearanceObserver.stop() } }
- จำลองส่วนการแสดงข้อมูลเอาไว้ และกำหนด controller ข้างต้นเข้าไปใน Turbo Frame
# demo.html.erb <% 10.times.each do |index| %> <div class="py-10"> <%= turbo_frame_tag "graph_#{index}", data: { controller: "hyper", "hyper-src-value": snippet_path(id: index) } do %> <div class="border border-blue-300 shadow rounded-md p-4 max-w-sm w-full mx-auto"> ... </div> <% end %> </div> <% end %>
ผลลัพธ์ที่ได้ก็จะเป็นดังแสดงในรูปด้านล่าง จะเห็นได้ว่าข้อมูลจะค่อยๆ ทยอยแสดงขึ้นทีละส่วนตามที่ต้องการ
ทั้งนี้ทั้งนั้น สิ่งที่ทำมาทั้งหมดจริงๆ แล้ว Turbo Frame ของเรารองรับการแสดงข้อมูลแบบ Lazy Loading อยู่แล้ว ไม่จำเป็นต้องใช้ Stimulus เข้ามาช่วยแต่อย่างใด เพียงแค่เราระบุ src และ loading เป็น lazy เท่านั้นเอง
# demo.html.erb ... <%= turbo_frame_tag "graph_#{index}", src: snippet_path(id: index), loading: "lazy" do %> ...