การแสดงผลข้อมูลประเภทกราฟต่างๆ ในหน้า 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 %>
...