7 คอขวด JavaScript ที่ซ่อนอยู่ และวิธีแก้ไข
เว็บแอปที่ทำงานช้าไม่ค่อยเป็นเพราะอัลกอริทึมที่ไม่ดี แต่เป็นเพราะวิธีที่โค้ดของคุณสื่อสารกับเบราว์เซอร์ต่างหาก
ผมได้ทำการวิเคราะห์ (profile) แอปพลิเคชันที่ใช้งานจริงกว่า 300 แอป และพบว่า 73% ของปัญหาด้านประสิทธิภาพมาจาก 7 สาเหตุเหล่านี้
- Layout thrashing สิ่งนี้เกิดขึ้นเมื่อคุณอ่านค่า property หนึ่ง เขียนค่าลงใน DOM แล้วกลับมาอ่านค่าอีกครั้ง ซึ่งจะบังคับให้เบราว์เซอร์ต้องคำนวณ layout ใหม่ซ้ำแล้วซ้ำเล่า
- วิธีแก้ไข: รวบรวมการอ่านค่า (reads) ทั้งหมดไว้ก่อน จากนั้นจึงรวบรวมการเขียนค่า (writes) ทั้งหมดโดยใช้
requestAnimationFrame
- Unbounded event listeners การเพิ่ม event listener โดยไม่มีการลบออกจะทำให้เกิด memory leaks ซึ่งเป็นปัญหาใหญ่ใน single-page apps
- วิธีแก้ไข: ใช้
AbortControllerเพื่อล้าง listener เมื่อ component ถูก unmount
- การอ่าน DOM แบบ synchronous ใน loop
การอ่านค่า
offsetWidthหรือgetBoundingClientRectภายใน loop จะกระตุ้นให้เกิด reflow อย่างต่อเนื่อง
- วิธีแก้ไข: เก็บค่า layout ไว้ในตัวแปร (cache) ก่อนที่จะเริ่มทำงานใน loop
- การขาดการทำ batching ด้วย requestAnimationFrame การเปลี่ยนแปลง DOM โดยตรงเมื่อเกิดเหตุการณ์ scroll หรือ resize จะทำงานบ่อยเกินไป ซึ่งทำให้เกิดอาการกระตุก (jank)
- วิธีแก้ไข: ใช้ตัวแปร ticking และ
requestAnimationFrameเพื่อซิงค์การอัปเดตให้เข้ากับรอบการวาดภาพ (paint cycle)
- ข้อมูล JSON.parse ที่มีขนาดใหญ่เกินไป การ parse ไฟล์ขนาดใหญ่จะไปบล็อก main thread ซึ่งทำให้เกิดอาการหน่วงในการตอบสนอง (input lag)
- วิธีแก้ไข: ใช้ Web Workers เพื่อ parse ข้อมูลแยกออกจาก main thread
- การจับคู่ CSS selector ที่ซับซ้อน Selector ที่ซ้อนกันลึกๆ หรือมีความซับซ้อนจะทำให้การคำนวณสไตล์ (style recalculations) ช้าลง
- วิธีแก้ไข: ใช้ Lighthouse เพื่อหา layout shifts และทำให้ selector ของคุณเรียบง่ายขึ้น
- Bundle chunks ที่ซ้ำซ้อน Bundle ที่มีขนาดใหญ่และไม่ได้รับการปรับแต่งจะทำให้เสียเวลาในการรับส่งข้อมูล
- วิธีแก้ไข: ใช้
webpack-bundle-analyzerเพื่อค้นหาและลบโค้ดที่ซ้ำซ้อนออก
วิธีวัดความคืบหน้าของคุณ:
- เปิด Chrome DevTools
- ไปที่แท็บ Performance
- บันทึกการทำงาน (record) เป็นเวลา 5 วินาที
- มองหา task ที่ใช้เวลานานกว่า 50ms ใน Main flame chart
- ลองแก้ไขหนึ่งจุดแล้วเปรียบเทียบเวลา Rendering และ Painting
การแก้ไขปัญหาเหล่านี้จะช่วยปรับปรุง Core Web Vitals ของคุณ โดยเฉพาะอย่างยิ่ง Largest Contentful Paint และ Interaction to Next Paint