เช็คลิสต์ประสิทธิภาพ UI ผู้ดูแล Vue 3 สำหรับรายการหนักที่เร็วขึ้น
ใช้เช็คลิสต์ประสิทธิภาพ Vue 3 นี้เพื่อเร่งรายการหนักด้วย virtualization, ดีบาวซ์การค้นหา, คอมโพเนนต์ memoized และสถานะการโหลดที่ดีกว่า

ทำไมรายการหนักในหน้าแผงผู้ดูแลถึงรู้สึกช้า
ผู้ใช้ไม่ค่อยพูดว่า "คอมโพเนนต์นี้ไม่ประหยัดทรัพยากร" พวกเขาจะบอกว่าหน้าจอรู้สึกหน่วง: เลื่อนกระตุก การพิมพ์หน่วง และการคลิกตอบช้า แม้ข้อมูลจะถูกต้อง แต่ความหน่วงนั้นทำให้คนลังเลและหยุดเชื่อใจเครื่องมือ
หน้าแผงผู้ดูแลหนาขึ้นเร็วเพราะรายการไม่ใช่แค่ "รายการธรรมดา" ตารางเดียวอาจมีแถวเป็นพัน คอลัมน์เยอะ และเซลล์แบบกำหนดเองที่มีแบดจ์ เมนู อวาตาร์ ทูลทิป และตัวแก้ไขอินไลน์ บวกการเรียงลำดับ ตัวกรองหลายตัว และการค้นหาสด หน้าจะเริ่มทำงานหนักในทุกการเปลี่ยนแปลงเล็กๆ
สิ่งที่ผู้คนมักสังเกตเห็นก่อนคือเรื่องง่ายๆ: การเลื่อนลดเฟรม การค้นหารู้สึกตามไม่ทันนิ้ว เมนูแถวเปิดช้า เลือกแบบกลุ่มค้าง และสถานะการโหลดกะพริบหรือรีเซ็ตหน้า
เบื้องหลังรูปแบบก็เรียบง่ายเช่นกัน: มีสิ่งต่างๆ ถูกเรนเดอร์ซ้ำบ่อยเกินไป แค่กดคีย์เดียวก็ทริกเกอร์การกรอง การกรองทำให้ตารางอัปเดต และทุกแถวก็สร้างเซลล์ใหม่ หากแต่ละแถวเบา คุณจะรอด แต่ถ้าแต่ละแถวเหมือนเป็นมินิแอพ คุณต้องจ่ายค่าใช้จ่ายทุกครั้ง
เช็คลิสต์ประสิทธิภาพ Vue 3 สำหรับแผงผู้ดูแลไม่ใช่การชนะแข่งขันเบนช์มาร์ก แต่มุ่งให้การพิมพ์ลื่น เลื่อนนิ่ง คลิกตอบสนอง และเห็นความคืบหน้าโดยไม่รบกวนผู้ใช้
ข่าวดี: การเปลี่ยนแปลงเล็กๆ มักชนะการเขียนใหม่ใหญ่ เรนเดอร์แถวน้อยลง (virtualization), ลดงานต่อคีย์สโตรก (debounce), กันไม่ให้เซลล์ที่แพงทำงานซ้ำ (memoization), และออกแบบสถานะการโหลดที่ไม่ทำให้หน้าเด้ง
วัดก่อนเปลี่ยนแปลง
ถ้าปรับโดยไม่มีฐานอ้างอิง คุณอาจไปแก้สิ่งที่ผิด เลือกหน้าช้าๆ หน้าหนึ่ง (เช่นตารางผู้ใช้ คิวตั๋ว หรือรายการคำสั่งซื้อ) และกำหนดเป้าหมายที่คุณรู้สึกได้: เลื่อนลื่นและการพิมพ์ไม่หน่วง
เริ่มจากทำให้เหตุการณ์ช้าซ้ำได้ แล้วโปรไฟล์มัน
บันทึกช่วงสั้นๆ ในแผง Performance ของเบราว์เซอร์: โหลดรายการ เลื่อนแรงสักไม่กี่วินาที แล้วพิมพ์ในช่องค้นหา มองหางานยาวบน main thread และงาน layout/paint ที่ซ้ำเมื่อไม่ควรจะมีอะไรเกิดขึ้น
แล้วเปิด Vue Devtools เพื่อตรวจดูว่าอะไรจริงๆ ถูกรีเรนเดอร์ หากแค่กดคีย์เดียวทำให้ทั้งตาราง ตัวกรอง และเฮดเดอร์ของหน้ารีเรนเดอร์ นั่นมักอธิบายการหน่วงของการพิมพ์ได้
ติดตามตัวเลขไม่กี่ค่าเพื่อยืนยันการปรับปรุงทีหลัง:
- เวลาไปยังรายการที่ใช้งานครั้งแรก (ไม่ใช่แค่สปินเนอร์)
- ความรู้สึกขณะเลื่อน (ลื่น vs กระตุก)
- ความหน่วงของการพิมพ์ (ตัวอักษรปรากฏทันทีไหม)
- ระยะเวลาเรนเดอร์ของคอมโพเนนต์ตาราง
- เวลาเครือข่ายของการเรียก API รายการ
สุดท้าย ยืนยันว่าคอขวดอยู่ที่ไหน การทดสอบง่ายๆ คือการลดเสียงเครือข่าย ถ้า UI ยังคงกระตุกกับข้อมูลที่แคชไว้ ส่วนใหญ่เป็นปัญหาการเรนเดอร์ หาก UI ลื่นแต่ผลลัพธ์มาช้า ให้มุ่งเรื่องเครือข่าย ขนาดคิวรี และการกรองฝั่งเซิร์ฟเวอร์
ทำ virtualization กับรายการและตารางขนาดใหญ่
Virtualization มักเป็นตัวให้ผลลัพธ์มากที่สุดเมื่อหน้าผู้ดูแลเรนเดอร์แถวเป็นร้อยหรือพัน แทนที่จะใส่ทุกแถวใน DOM คุณเรนเดอร์เฉพาะส่วนที่มองเห็น (บวก buffer เล็กน้อย) ซึ่งลดเวลาเรนเดอร์ ลดการใช้หน่วยความจำ และทำให้การเลื่อนนิ่งขึ้น
เลือกแนวทางที่เหมาะสม
Virtual scrolling (windowing) เหมาะเมื่อต้องการให้ผู้ใช้เลื่อนผ่านชุดข้อมูลยาวๆ ได้ลื่น Pagination เหมาะเมื่อต้องกระโดดเป็นหน้าทั่วไปและต้องการคิวรีฝั่งเซิร์ฟเวอร์ง่ายๆ รูปแบบ "โหลดเพิ่ม" เหมาะเมื่ออยากมีคอนโทรลน้อยลงแต่ยังหลีกเลี่ยง DOM ใหญ่ๆ
กฎคร่าวๆ:
- 0–200 แถว: เรนเดอร์ปกติยังโอเค
- 200–2,000 แถว: ใช้ virtualization หรือ pagination ขึ้นกับ UX
- 2,000+ แถว: virtualization พร้อมการกรอง/เรียงลำดับฝั่งเซิร์ฟเวอร์
ทำให้ virtualization เสถียร
ลิสต์แบบ virtual ทำงานดีที่สุดเมื่อแต่ละแถวมีความสูงที่คาดการณ์ได้ ถ้าความสูงแถวเปลี่ยนหลังการเรนเดอร์ (รูปโหลด ข้อความขึ้นบรรทัดใหม่ ส่วนที่ขยาย) ตัวเลื่อนต้องวัดใหม่ นำไปสู่การเลื่อนกระโดดและ layout thrash
รักษาให้เสถียร:
- ใช้ความสูงแถวคงที่เมื่อทำได้ หรือชุดความสูงที่รู้จักไม่กี่แบบ
- ตัดเนื้อหาที่เปลี่ยนได้ (แท็ก โน้ต) แล้วแสดงแบบรายละเอียด
- ใช้คีย์ที่แข็งแรงและเป็นเอกลักษณ์สำหรับแต่ละแถว (อย่าใช้ index ของอาเรย์)
- สำหรับเฮดเดอร์แบบ sticky ให้เก็บเฮดเดอร์ไว้ด้านนอกของร่างกายที่ virtualized
- หากต้องรองรับความสูงไม่คงที่ ให้เปิดการวัดและเก็บเซลล์ให้เรียบง่าย
ตัวอย่าง: ถ้าตารางตั๋วมี 10,000 แถว ให้ virtualize ส่วน body ของตารางและรักษาความสูงแถวให้คงที่ (status, subject, assignee) เก็บข้อความยาวไว้หลังลิ้นชักรายละเอียดเพื่อให้การเลื่อนลื่น
ดีบาวซ์การค้นหาและการกรองที่ชาญฉลาด
ช่องค้นหาอาจทำให้ตารางเร็วๆ รู้สึกช้า ปัญหามักไม่ใช่การกรองเอง แต่วงปฏิกิริยาที่เกิดขึ้น: แต่ละคีย์สโตรกทริกเกอร์การเรนเดอร์ watcher และบ่อยครั้งเป็นคำขอ
Debounce หมายถึง "รอสักครู่หลังผู้ใช้หยุดพิมพ์ แล้วทำครั้งเดียว" สำหรับหน้าผู้ดูแลส่วนใหญ่ 200–400 ms ให้ความรู้สึกตอบสนองโดยไม่กระตุกเกินไป พิจารณาตัดช่องว่างและละเว้นการค้นหาที่สั้นกว่า 2–3 ตัวอักษรถ้าข้อมูลของคุณรองรับ
กลยุทธ์การกรองควรตรงกับขนาดข้อมูลและกฎรอบๆ มัน:
- ถ้าน้อยกว่าสองร้อยแถวและโหลดเรียบร้อย การกรองฝั่งไคลเอนต์พอใช้ได้
- ถ้าเป็นพันแถวหรือมีสิทธิ์เข้มงวด ให้คิวรีฝั่งเซิร์ฟเวอร์
- ถ้าตัวกรองแพง (ช่วงวันที่ ตรรกะสถานะ) ให้ย้ายไปฝั่งเซิร์ฟเวอร์
- หากต้องการทั้งสองแบบ ให้ทำแบบผสม: กรองคร่าวๆ ฝั่งไคลเอนต์ แล้วคิวรีฝั่งเซิร์ฟเวอร์สำหรับผลสุดท้าย
เมื่อเรียกเซิร์ฟเวอร์ ให้จัดการผลลัพธ์ล้าสมัย หากผู้ใช้พิมพ์ "inv" แล้วเร็วๆ นี้พิมพ์จบเป็น "invoice" คำขอเก่าอาจกลับมาทีหลังและเขียนทับ UI ด้วยข้อมูลผิด พัก/ยกเลิกคำขอก่อนหน้า (AbortController กับ fetch หรือฟีเจอร์ยกเลิกของไคลเอนต์ HTTP ของคุณ) หรือเก็บ id ของคำขอและเพิกเฉยต่อผลลัพธ์ที่ไม่ใช่คำขอตัวล่าสุด
สถานะการโหลดสำคัญเท่าๆ กับความเร็ว หลีกเลี่ยงสปินเนอร์เต็มหน้าสำหรับทุกคีย์สโตรก โฟลว์ที่สงบจะเป็นแบบนี้: ขณะพิมพ์ อย่าแฟลชอะไร เมื่อแอพกำลังค้นหา ให้แสดงอินดิเคเตอร์เล็กๆ ใกล้อินพุต เมื่อผลอัปเดตให้แสดงข้อความชัดเจนแต่สุภาพเช่น "แสดงผล 42 รายการ" หากไม่มีผล ให้แสดง "ไม่พบผล" แทนการทิ้งกริดว่าง
คอมโพเนนต์ memoized และการเรนเดอร์ที่เสถียร
หลายตารางผู้ดูแลที่ช้าจริงๆ ไม่ได้ช้าเพราะ "ข้อมูลมากเกินไป" แต่เพราะเซลล์เดิมถูกรีเรนเดอร์ซ้ำๆ
หาให้เจอว่าอะไรทำให้รีเรนเดอร์
การอัปเดตซ้ำมักมาจากนิสัยไม่กี่อย่าง:
- ส่งออบเจ็กต์ reactive ขนาดใหญ่เป็น props ในเมื่อต้องการแค่ไม่กี่ฟิลด์
- สร้างฟังก์ชันอินไลน์ในเทมเพลต (สร้างใหม่ทุกเรนเดอร์)
- ใช้ watcher แบบลึกบนอาเรย์ใหญ่หรือออบเจ็กต์แถว
- สร้างอาเรย์หรือออบเจ็กต์ใหม่ในเทมเพลตสำหรับแต่ละเซลล์
- ทำงานฟอร์แมตภายในแต่ละเซลล์ (วันที่ สกุลเงิน การพาร์ส) ทุกการอัปเดต
เมื่อ props และ handlers เปลี่ยน identity, Vue จะถือว่า child อาจต้องอัปเดต แม้จะไม่มีอะไรเปลี่ยนแปลงด้านมองเห็น
ทำให้ props เสถียร แล้วค่อย memoize
เริ่มจากส่ง props ที่เล็กลงและเสถียร แทนที่จะส่ง row object ทั้งหมดเข้าไปในทุกเซลล์ ให้ส่ง row.id บวกฟิลด์เฉพาะที่เซลล์แสดง ย้ายค่าที่ได้จากการคำนวณไปไว้ใน computed เพื่อให้คำนวณใหม่เฉพาะเมื่ออินพุตเปลี่ยน
หากส่วนของแถวเปลี่ยนแปลงน้อย v-memo ช่วยได้ Memoize ส่วนที่นิ่งตามอินพุตที่เสถียร (เช่น row.id และ row.status) เพื่อการพิมพ์หรือโฮเวอร์จะไม่บังคับให้ทุกเซลล์รันเทมเพลตใหม่
นอกจากนี้เก็บงานแพงให้ออกนอกเส้นทางการเรนเดอร์ ฟอร์แมตวันที่ครั้งเดียว (เช่นในแผนที่ computed ที่มีคีย์เป็น id) หรือฟอร์แมตบนเซิร์ฟเวอร์เมื่อสมเหตุสมผล ชนะจริงที่เจอบ่อยคือหยุดคอลัมน์ "อัปเดตล่าสุด" จากการเรียก new Date() สำหรับหลายร้อยแถวในทุกการอัปเดต UI
เป้าหมายชัดเจน: รักษา identity ให้เสถียร ดึงงานออกจากเทมเพลต และอัปเดตเฉพาะสิ่งที่เปลี่ยนจริงๆ
สถานะการโหลดที่ชาญฉลาดซึ่งรู้สึกเร็ว
รายการมักรู้สึกช้ากว่าที่เป็นจริงเพราะ UI กระโดดไปมา สถานะการโหลดที่ดีทำให้การรอคาดเดาได้
สเกเลตอนแถวช่วยเมื่อรูปร่างข้อมูลเป็นที่รู้ (ตาราง การ์ด ไทม์ไลน์) สปินเนอร์ไม่บอกว่ากำลังรออะไร สเกเลตอนตั้งความคาดหวัง: มีกี่แถว ตำแหน่งปุ่มจะอยู่ตรงไหน และเลย์เอาต์จะเป็นอย่างไร
เมื่อรีเฟรชข้อมูล (เปลี่ยนหน้า เรียงลำดับ ตัวกรอง) ให้เก็บผลลัพธ์ก่อนหน้าบนหน้าจอขณะคำขอใหม่ทำงาน แสดงคำใบ้ "กำลังอัปเดต" เล็กๆ แทนการล้างตาราง ผู้ใช้จะยังอ่านหรือตรวจสอบบางอย่างได้ขณะอัปเดต
การโหลดบางส่วนชนะการบล็อกทั้งหมด
ไม่ใช่ทุกอย่างต้องหยุด หากตารางกำลังโหลด ให้เก็บแถบตัวกรองไว้แต่แค่ปิดการใช้ชั่วคราว หากการกระทำของแถวต้องการข้อมูลเพิ่ม ให้แสดงสถานะรอดำเนินการบนแถวนั้น ไม่ใช่ทั้งหน้า
รูปแบบที่เสถียรจะเป็นแบบนี้:
- โหลดครั้งแรก: แถวสเกเลตอน
- รีเฟรช: เก็บแถวเก่าไว้ แสดงคำใบ้ "กำลังอัปเดต" เล็กๆ
- ตัวกรอง: ปิดชั่วคราวระหว่างการดึง แต่ไม่ย้ายจุดวาง
- การกระทำต่อแถว: สถานะรอดำเนินการของแต่ละแถว
- ข้อผิดพลาด: แสดงแบบอินไลน์โดยไม่ยุบเลย์เอาต์
ป้องกันการเปลี่ยนแปลงเลย์เอาต์
กันพื้นที่สำหรับทูลบาร์ สเตตว่าง และการแบ่งหน้าเพื่อไม่ให้คอนโทรลเลื่อนไปเมื่อผลเปลี่ยน ความสูงขั้นต่ำคงที่สำหรับพื้นที่ตารางช่วยได้ และการทำให้เฮดเดอร์/แถบตัวกรองเรนเดอร์ตลอดเวลาเลี่ยงการเด้งของหน้า
ตัวอย่างชัดเจน: บนหน้าตั๋ว การสลับจาก "Open" เป็น "Solved" ไม่ควรล้างรายการ ควรเก็บแถวปัจจุบัน ปิดตัวกรองสถานะชั่วคราว และแสดงสถานะรอดำเนินการเฉพาะตั๋วที่อัปเดต
ขั้นตอนทีละน้อย: แก้หน้าช้าให้เสร็จในบ่ายเดียว
เลือกหน้าช้าและทำเหมือนซ่อมเล็กๆ เป้าหมายไม่ใช่ความสมบูรณ์แบบ แต่เป็นการปรับปรุงที่เห็นได้ชัดในเรื่องเลื่อนและการพิมพ์
แผนบ่ายสั้นๆ
ตั้งชื่อปัญหาให้ชัด เปิดหน้าขึ้นแล้วทำสามอย่าง: เลื่อนเร็วๆ พิมพ์ในช่องค้นหา และเปลี่ยนหน้า/ตัวกรอง มักจะมีเพียงหนึ่งอย่างที่เสียจริง และนั่นบอกคุณว่าจะแก้อะไรก่อน
จากนั้นทำตามลำดับง่ายๆ:
- ระบุคอขวด: เลื่อนกระตุก การพิมพ์ช้า การตอบสนองเครือข่ายช้า หรือผสม
- ลดขนาด DOM: virtualization หรือลดขนาดหน้าตั้งต้นจน UI เสถียร
- ทำให้การค้นสงบ: ดีบาวซ์อินพุตและยกเลิกคำขอก่อนหน้า
- ทำให้แถวเสถียร: คีย์คงที่ ไม่สร้างออบเจ็กต์ใหม่ในเทมเพลต, memoize การเรนเดอร์แถวเมื่อข้อมูลไม่เปลี่ยน
- ปรับความรู้สึกความเร็ว: สเกเลตอนระดับแถวหรือสปินเนอร์อินไลน์เล็กๆ แทนการบล็อกทั้งหน้า
หลังแต่ละขั้น ให้ทดสอบการกระทำเดิมที่รู้สึกแย่ หาก virtualization ทำให้เลื่อนไหล ให้ไปต่อ หากการพิมพ์ยังหน่วง ดีบาวซ์และการยกเลิกคำขอมักเป็นชัยชนะถัดไป
ตัวอย่างเล็กๆ ที่คัดลอกได้
สมมติว่ามีตาราง "Users" 10,000 แถว การเลื่อนกระตุกเพราะเบราว์เซอร์ต้องเพนท์หลายแถว Virtualize เพื่อให้เฉพาะแถวที่มองเห็นเรนเดอร์
จากนั้นการค้นหาหน่วงเพราะทุกคีย์สโตรกทริกเกอร์คำขอ เพิ่มดีบาวซ์ 250–400 ms และยกเลิกคำขอก่อนหน้าด้วย AbortController (หรือการยกเลิกของ HTTP client) เพื่อให้เฉพาะคิวรีล่าสุดอัปเดตรายการ
สุดท้าย ทำให้แต่ละแถวถูกลง: ส่ง props เรียบง่าย (id และ primitive เมื่อเป็นไปได้), memoize เอาต์พุตของแถวเพื่อแถวที่ไม่เกี่ยวข้องไม่ต้องวาดใหม่, และแสดงการโหลดภายใน body ของตารางแทนโอเวอร์เลย์เต็มหน้าจอเพื่อให้หน้าตอบสนองได้
ข้อผิดพลาดทั่วไปที่ทำให้ UI ช้าต่อเนื่อง
ทีมมักใช้การแก้ไขไม่กี่อย่าง เห็นผลเล็กน้อย แล้วติดอยู่ เหตุผลปกติคือส่วนแพงไม่ใช่ "รายการ" แต่นั่นคือทุกอย่างที่แต่ละแถวทำขณะเรนเดอร์ อัปเดต และดึงข้อมูล
Virtualization ช่วย แต่ลบผลมันออกได้ง่าย: หากทุกแถวที่มองเห็นยังคง mount ชาร์ตหนัก ถอดรหัสรูป ทำ watcher เยอะ หรือฟอร์แมตแพง การเลื่อนก็ยังรู้สึกหยาบ Virtualization จำกัดจำนวนแถวที่มีอยู่ ไม่ใช่น้ำหนักของแต่ละแถว
คีย์เป็นอีกฆาตกรเงียบ หากใช้ index ของอาเรย์เป็นคีย์ Vue จะตามแถวไม่ถูกเมื่อต้องแทรก ลบ หรือเรียงลำดับ ซึ่งมักทำให้ remount และรีเซ็ตโฟกัสอินพุต ใช้ id ที่เสถียรเพื่อให้ Vue สามารถ reuse DOM และอินสแตนซ์คอมโพเนนต์ได้
ดีบาวซ์ก็หลอกลวงได้ หากดีเลย์ยาวเกินไป UI จะรู้สึกพัง: คนพิมพ์แล้วไม่มีอะไรเกิดขึ้นแล้วผลกระโดดขึ้นมา ดีเลย์สั้นๆ มักทำงานดีกว่า และยังสามารถแสดงฟีดแบ็กทันทีเช่น "กำลังค้นหา..." เพื่อให้ผู้ใช้รู้ว่าแอพได้ยิน
ห้าเรื่องที่มักพบในการตรวจสอบรายการช้า:
- ใช้ virtualization แต่ยังใส่เซลล์หนัก (รูป ชาร์ต คอมโพเนนต์ซับซ้อน) ในทุกแถวที่มองเห็น
- ใช้คีย์เป็น index ทำให้แถว remount เมื่อเรียงหรืออัปเดต
- ดีบาวซ์การค้นหายาวเกินไปจนรู้สึกหน่วง
- ทริกเกอร์คำขอจากการเปลี่ยน reactive กว้างๆ (watch ทั้งออบเจ็กต์ filter, ซิงก์สถานะใน URL บ่อยเกิน)
- ใช้ตัวโหลดหน้าแบบ global ที่ล้างตำแหน่งสกอล์และแย่งโฟกัส
ถ้าคุณใช้เช็คลิสต์ประสิทธิภาพ Vue 3 ให้ถือว่า "อะไรรีเรนเดอร์" และ "อะไรรีเฟตช์" เป็นปัญหาชั้นหนึ่ง
เช็คลิสต์ด่วนด้านประสิทธิภาพ
ใช้เช็คลิสต์นี้เมื่อเทเบิลหรือรายการเริ่มรู้สึกหน่วง เป้าหมายคือการเลื่อนลื่น การค้นหาที่คาดเดาได้ และการรีเรนเดอร์ที่น้อยลง
การเรนเดอร์และการเลื่อน
ปัญหา "รายการช้า" ส่วนใหญ่เกิดจากการเรนเดอร์มากเกินไปบ่อยเกินไป
- ถ้าหน้าสามารถแสดงแถวเป็นร้อย ให้ใช้ virtualization เพื่อให้ DOM มีเฉพาะสิ่งที่อยู่บนหน้าจอ (บวก buffer เล็กน้อย)
- รักษาความสูงแถวให้คงที่ ความสูงที่เปลี่ยนได้ทำลาย virtualization และทำให้เกิดจังค์
- หลีกเลี่ยงการส่งออบเจ็กต์และอาเรย์ใหม่เป็น props อินไลน์ (เช่น
:style="{...}") สร้างมันครั้งเดียวแล้วนำกลับมาใช้ - ระวัง watcher แบบลึกบนข้อมูลแถว ใช้ computed และ watch เฉพาะฟิลด์ที่เปลี่ยนจริง
- ใช้คีย์ที่เสถียรตรงกับ id ของเรคอร์ด แทน index ของอาเรย์
การค้นหา การโหลด และคำขอ
ทำให้รายการรู้สึกเร็วแม้เครือข่ายจะไม่เร็ว
- ดีบาวซ์การค้นหาราว 250–400 ms เก็บโฟกัสในอินพุต และยกเลิกคำขอล้าสมัยเพื่อไม่ให้ผลเก่ามาเขียนทับผลใหม่
- เก็บผลลัพธ์เดิมไว้ในขณะโหลดผลใหม่ ใช้สถานะ "กำลังอัปเดต" เล็กๆ แทนการล้างตาราง
- รักษาการแบ่งหน้าให้คาดเดาได้ (ขนาดหน้าคงที่ พฤติกรรมถัด/ก่อนชัดเจน ไม่มีการรีเซ็ตที่ไม่คาดคิด)
- รวมคำขอที่เกี่ยวข้อง (เช่น นับ + ข้อมูลรายการ) หรือตั้งให้ดึงพร้อมกันแล้วเรนเดอร์ทีเดียว
- แคชการตอบกลับที่สำเร็จล่าสุดสำหรับชุดตัวกรองเพื่อให้การกลับไปมาที่มุมมองที่ใช้บ่อยรู้สึกทันที
ตัวอย่าง: หน้าตั๋วภายใต้โหลดสูง
ทีมซัพพอร์ตเปิดหน้าตั๋วทั้งวัน พวกเขาค้นหาตามชื่อลูกค้า แท็ก หรือลำดับคำสั่ง ขณะเดียวกันฟีดสดอัปเดตสถานะตั๋ว (การตอบกลับใหม่ การเปลี่ยนความสำคัญ ตัวจับเวลา SLA) ตารางอาจไปถึง 10,000 แถว
เวอร์ชันแรกทำงานได้แต่รู้สึกแย่ ขณะพิมพ์ตัวอักษรปรากฏช้าตารางกระโดดไปบนสุด ตำแหน่งสกอล์รีเซ็ต แอพส่งคำขอทุกคีย์สโตรก ผลกะพริบระหว่างเก่าและใหม่
สิ่งที่เปลี่ยน:
- ดีบาวซ์อินพุตการค้นหา (250–400 ms) และคิวรีเฉพาะเมื่อผู้ใช้หยุดพิมพ์
- เก็บผลลัพธ์ก่อนหน้าไว้ขณะคำขอใหม่ทำงาน
- virtualize แถวเพื่อให้ DOM เรนเดอร์เฉพาะที่มองเห็น
- memoize แถวตั๋วเพื่อไม่ให้รีเรนเดอร์สำหรับอัปเดตสดที่ไม่เกี่ยวข้อง
- โหลดเนื้อหาเซลล์หนัก (อวาตาร์ สนิปเพ็ตที่มีความสมบูรณ์ ทูลทิป) แบบ lazy เมื่อแถวนั้นมองเห็น
หลังดีบาวซ์ การหน่วงการพิมพ์หายไปและคำขอที่เสียเปล่าลดลง การเก็บผลก่อนหน้าหยุดการกะพริบ ทำให้หน้าดูเสถียรแม้เครือข่ายช้า
Virtualization เป็นผลลัพธ์เชิงสายตาที่ใหญ่ที่สุด: การเลื่อนลื่นเพราะเบราว์เซอร์ไม่ต้องจัดการหลายพันแถวพร้อมกัน Memoizing แถวป้องกันการอัปเดตทั้งตารางเมื่อมีตั๋วเดียวเปลี่ยน
อีกการปรับคือการแบทช์อัปเดตจากฟีดสดและใช้ทุกๆ ไม่กี่ร้อยมิลลิวินาทีเพื่อไม่ให้ UI รีโฟลว์บ่อยเกินไป
ผลลัพธ์: เลื่อนนิ่ง การพิมพ์เร็ว และความประหลาดใจน้อยลง
ขั้นตอนถัดไป: ทำให้ประสิทธิภาพเป็นค่าเริ่มต้น
UI ผู้ดูแลที่เร็วเก็บรักษาง่ายกว่าการกู้คืนทีหลัง ให้ถือเช็คลิสต์นี้เป็นมาตรฐานสำหรับทุกหน้ใหม่ ไม่ใช่การทำความสะอาดครั้งเดียว
ให้ความสำคัญกับการแก้ที่ผู้ใช้รู้สึกก่อน รางวัลใหญ่มักมาจากการลดสิ่งที่เบราว์เซอร์ต้องวาดและความเร็วในการตอบสนองต่อการพิมพ์
เริ่มจากเบสิก: ลดขนาด DOM (virtualize รายการยาว อย่าเรนเดอร์แถวที่ซ่อนอยู่), ลดความหน่วงของอินพุต (ดีบาวซ์การค้นหา ย้ายการกรองหนักออกจากทุกคีย์สโตรก), แล้วทำให้การเรนเดอร์เสถียร (memoize คอมโพเนนต์แถว รักษา props ให้คงที่) เก็บรีแฟคเตอร์เล็กๆ ไว้ทีหลัง
หลังจากนั้น เพิ่มเกราะป้องกันเพื่อไม่ให้หน้ารายการใหม่ถอยหลัง เช่น รายการเกิน 200 แถวต้องใช้ virtualization, ทุกอินพุตค้นหาต้องดีบาวซ์, และทุกแถวต้องใช้คีย์ id ที่เสถียร
บล็อกที่ใช้ซ้ำได้ช่วยให้ทำได้ง่ายขึ้น คอมโพเนนต์ตาราง virtual ที่มีค่าเริ่มต้นที่เหมาะสม, แถบค้นหาพร้อมดีบาวซ์ในตัว, และสเกเลตอน/สถานะว่างที่ตรงกับเลย์เอาต์ของตาราง มีประโยชน์มากกว่าหน้า wiki
นิสัยปฏิบัติ: ก่อน merge หน้าผู้ดูแลใหม่ ให้ทดสอบกับข้อมูล 10x และ preset เครือข่ายช้า หากยังรู้สึกดี แปลว่ามันจะทำงานได้ดีในการใช้งานจริง
ถ้าคุณสร้างเครื่องมือภายในอย่างรวดเร็วและอยากให้รูปแบบเหล่านี้คงที่ในทุกหน้า AppMaster (appmaster.io) อาจเป็นตัวเลือกที่ดี มันสร้างแอพ Vue 3 จริง ดังนั้นแนวทางการโปรไฟล์และการปรับจูนยังคงใช้ได้เมื่อรายการหนักขึ้น
คำถามที่พบบ่อย
เริ่มจาก virtualization หากคุณแสดงมากกว่าหลายร้อยแถวพร้อมกัน มักจะให้ความรู้สึกดีขึ้นมากเพราะเบราว์เซอร์ไม่ต้องจัดการกับโหนด DOM จำนวนพันๆ ขณะเลื่อนหน้าจอ
เมื่อการเลื่อนหน้าตกเฟรม มักเป็นปัญหาจากการเรนเดอร์/DOM หาก UI ยังคงเรียบแต่ผลลัพธ์มาถึงช้า ปัญหามักอยู่ที่เครือข่ายหรือการกรองฝั่งเซิร์ฟเวอร์; ยืนยันโดยการทดสอบกับข้อมูลแคชหรือการตอบกลับจากเครื่องท้องถิ่นที่เร็ว
Virtualization จะเรนเดอร์เฉพาะแถวที่มองเห็นได้ (บวก buffer เล็กน้อย) แทนที่จะใส่ทุกแถวลงใน DOM ซึ่งลดขนาด DOM การใช้หน่วยความจำ และงานที่ Vue กับเบราว์เซอร์ต้องทำขณะเลื่อน
ตั้งความสูงแถวให้สม่ำเสมอและหลีกเลี่ยงคอนเทนต์ที่เปลี่ยนขนาดหลังการเรนเดอร์ หากแถวขยาย ตัวหรือตำแหน่งรูปเปลี่ยน เบราว์เซอร์จะต้องวัดใหม่และทำให้การเลื่อนกระโดดได้
ค่าดีบาวซ์ที่ดีเริ่มต้นราว 250–400 ms ซึ่งสั้นพอให้รู้สึกเร็ว แต่ยาวพอที่จะหลีกเลี่ยงการกรองและการเรนเดอร์ซ้ำในทุกคีย์สโตรก
ยกเลิกคำขอก่อนหน้าหรือเพิกเฉยต่อการตอบกลับที่ล้าสมัย เป้าหมายคือให้เฉพาะคำขอปัจจุบันเท่านั้นที่อัพเดตตาราง เพื่อไม่ให้การตอบสนองเก่ากลับมาเขียนทับผลใหม่
หลีกเลี่ยงการส่งออบเจ็กต์ reactive ขนาดใหญ่เมื่อต้องการเฉพาะไม่กี่ฟิลด์ และหลีกเลี่ยงการสร้างฟังก์ชันหรือออบเจ็กต์ใหม่ในเทมเพลต เมื่อ identity ของ props และ handler คงที่ ให้ใช้การ memo เช่น v-memo กับส่วนแถวที่ไม่เปลี่ยนแปลง
ย้ายงานที่แพงออกจากเส้นทางการเรนเดอร์เพื่อไม่ให้มันรันสำหรับทุกแถวที่มองเห็น คำนวณหรือแคชค่าที่ฟอร์แมตแล้ว (เช่นวันที่หรือสกุลเงิน) และนำกลับมาใช้จนกว่าข้อมูลต้นทางจะเปลี่ยน
เก็บผลลัพธ์เดิมไว้บนหน้าจอระหว่างการรีเฟรช และแสดงคำใบ้เล็กๆ ว่า "กำลังอัปเดต" แทนการล้างตารางเต็มหน้าจอ วิธีนี้หลีกเลี่ยงการกระพริบและการกระโดดของเลย์เอาต์ ทำให้หน้าดูตอบสนองเร็วขึ้นแม้เครือข่ายช้า
ใช่ เทคนิคเดียวกันใช้ได้เพราะ AppMaster สร้างแอพ Vue 3 จริง คุณยังต้องโปรไฟล์การเรนเดอร์, virtualize รายการยาว, ดีบาวซ์การค้นหา และทำให้การเรนเดอร์แถวเสถียร; ความแตกต่างคือคุณสามารถมาตรฐานรูปแบบเหล่านี้เป็นบล็อกที่นำกลับมาใช้ซ้ำได้


