7 คอขวดประสิทธิภาพ JavaScript ที่ซ่อนอยู่

เว็บแอปที่ทำงานช้าไม่ค่อยเป็นเพราะอัลกอริทึมที่ไม่ดี แต่เป็นเพราะวิธีที่โค้ดของคุณสื่อสารกับเบราว์เซอร์ต่างหาก

ผมได้ทำการวิเคราะห์ (profile) แอปพลิเคชันที่ใช้งานจริงมากกว่า 300 แอป และพบว่า 73% ของปัญหาด้านประสิทธิภาพมาจาก 7 สาเหตุเหล่านี้

  1. Layout thrashing สิ่งนี้เกิดขึ้นเมื่อคุณอ่านค่าคุณสมบัติเลย์เอาต์ (layout property) แล้วเขียนค่าลงใน DOM ทันทีหลังจากนั้น ซึ่งจะบังคับให้เบราว์เซอร์ต้องคำนวณเลย์เอาต์ใหม่หลายครั้ง • ผลกระทบ: การเรนเดอร์ช้าลง 40-60% • วิธีแก้ไข: ทำการอ่านค่าทั้งหมด (batch reads) ให้เสร็จก่อน จากนั้นจึงทำการเขียนค่าทั้งหมด (batch writes) โดยใช้ requestAnimationFrame

  2. Unbounded event listeners การเพิ่ม listener โดยไม่มีการลบออกจะทำให้เกิด memory leaks ซึ่งเป็นปัญหาใหญ่ใน single-page apps • ผลกระทบ: หน่วยความจำเพิ่มขึ้น 15-30% ต่อชั่วโมง • วิธีแก้ไข: ใช้ AbortController เพื่อล้าง listener เมื่อ component ถูก unmount

  3. Synchronous DOM reads in loops การอ่านคุณสมบัติอย่าง offsetWidth ภายในลูปที่มีการเขียนค่าลงใน DOM ด้วย จะทำให้เกิดการ reflow อย่างต่อเนื่อง • ผลกระทบ: เฟรมเรตตก (frame drops) 20-40% • วิธีแก้ไข: เก็บค่าเลย์เอาต์ไว้ในตัวแปร (cache) ก่อนที่จะเริ่มลูป

  4. Missing requestAnimationFrame batching การเปลี่ยนแปลง DOM โดยตรงเมื่อเกิดเหตุการณ์ scroll หรือ resize จะทำงานบ่อยเกินไป • ผลกระทบ: เกิดอาการกระตุก (jank) 10-25% ระหว่างการเลื่อนหน้าจอ • วิธีแก้ไข: ครอบ scroll handlers ของคุณด้วย requestAnimationFrame เพื่อให้สอดคล้องกับรอบการวาดภาพ (paint cycle)

  5. Large JSON.parse calls การ parse ไฟล์ JSON ขนาดใหญ่จะไปบล็อก main thread ซึ่งทำให้เกิดอาการ input lag • ผลกระทบ: เครื่องค้าง 50-200ms ต่อการเรียกใช้งานหนึ่งครั้ง • วิธีแก้ไข: ใช้ Web Workers เพื่อ parse ข้อมูลแยกออกจาก main thread

  6. Complex CSS selector matching Selector ที่ซ้อนกันลึกหรือมีความซับซ้อนจะทำให้การคำนวณสไตล์ใหม่ช้าลง • ผลกระทบ: ใช้เวลาในการคำนวณสไตล์เพิ่มขึ้น 5-15% • วิธีแก้ไข: ปรับโครงสร้าง CSS ให้เรียบง่ายขึ้นและใช้ selector ที่ไม่ซ้อนกันลึก (flatter selectors)

  7. Duplicate bundle chunks Bundle ที่ไม่ได้รับการปรับแต่งจะทำให้สิ้นเปลืองแบนด์วิดท์ • ผลกระทบ: สิ้นเปลืองการรับส่งข้อมูล 100-500KB • วิธีแก้ไข: ใช้เครื่องมืออย่าง webpack-bundle-analyzer เพื่อค้นหาและลบโค้ดที่ซ้ำซ้อนออก

วิธีวัดความคืบหน้าของคุณ: • เปิด Chrome DevTools และไปที่แท็บ Performance • บันทึกการทำงาน (record) เป็นเวลา 5 วินาที • มองหา long tasks ใน Main flame chart • ลองแก้ไขหนึ่งจุดแล้วเปรียบเทียบเวลา Rendering และ Painting

มุ่งเน้นไปที่ส่วนเหล่านี้เพื่อปรับปรุงคะแนน Core Web Vitals ของคุณ

ที่มา: https://dev.to/kui_luo/how-to-find-and-fix-7-hidden-performance-bottlenecks-in-your-javascript-code-ek5