JavaScript ที่ทำงานหนักอาจส่งผลเสียต่อทั้งประสิทธิภาพการโหลดหน้าเว็บและการโต้ตอบ เว็บแอปพลิเคชันสมัยใหม่พึ่งพา JavaScript จากหลายแหล่งข้อมูลมากกว่าที่เคย ซึ่งรวมถึงโค้ดของแอปพลิเคชันเอง (และโค้ดเฟรมเวิร์กที่ใช้) ตลอดจนสคริปต์ของบุคคลที่สามที่เพิ่มฟังก์ชันการทำงาน เช่น วิดเจ็ตแชทและวิดีโอเพลเยอร์ สคริปต์การวิเคราะห์และการตลาดที่ทำงานอยู่เบื้องหลังก็เป็นสาเหตุที่พบได้บ่อยและมักถูกมองข้าม
การระบุสาเหตุของปัญหาหน้าเว็บไม่ตอบสนองอาจเป็นเรื่องยาก เนื่องจากต้องใช้ความเชี่ยวชาญบางอย่างในการติดตามหรือทำโปรไฟล์ประสิทธิภาพของเว็บ รวมถึงวิธีตีความผลลัพธ์ นอกจากนี้ ข้อมูลภาคสนามมักจะแตกต่างจากข้อมูลในห้องทดลองอย่างมาก ซึ่งจะจำลองสถานการณ์ของผู้ใช้จริงเพียงส่วนเล็กๆ เท่านั้น ซึ่งอาจทำให้ระบุสาเหตุของปัญหาด้านประสิทธิภาพได้ยาก โดยเฉพาะอย่างยิ่งสำหรับการโต้ตอบ
Long Animation Frames API เป็น API น้ำหนักเบาที่ใช้ระบุ JavaScript ที่ทำงานหนักในภาคสนามได้ สคริปต์ที่ทำงานหนักอาจเป็นสคริปต์ที่ทำงานเป็นเวลานานเพียงสคริปต์เดียว หรือสคริปต์ที่ทำงานหลายครั้งในระหว่างวงจรของหน้าเว็บก็ได้
วิธีการนำไปใช้งาน
ระบบจะตรวจสอบเฟรมของภาพเคลื่อนไหวที่ใช้เวลานานโดยใช้อินเทอร์เฟซ PerformanceObserver โดยจะส่งรายการ long-animation-frame เมื่อเฟรมภาพเคลื่อนไหวใช้เวลาแสดงผลนานกว่า 50 มิลลิวินาที รายการนี้มีข้อมูลเกี่ยวกับเฟรมของภาพเคลื่อนไหวที่ใช้เวลานาน ซึ่งรวมถึงระยะเวลาของเฟรมและสคริปต์ที่ดำเนินการระหว่างเฟรม
รายการ long-animation-frame มีพร็อพเพอร์ตี้ scripts ซึ่งเป็นอาร์เรย์ของออบเจ็กต์ PerformanceScript ออบเจ็กต์ PerformanceScript แต่ละรายการมีข้อมูลเกี่ยวกับสคริปต์ที่ดำเนินการระหว่างเฟรมของภาพเคลื่อนไหวที่ใช้เวลานาน ซึ่งรวมถึง sourceURL และ duration ของสคริปต์
ตัวอย่างการระบุสคริปต์ที่ทำงานนานที่สุดซึ่งทำให้เกิดเฟรมของภาพเคลื่อนไหวที่ใช้เวลานาน
// Accumulate all script entries across the page lifecycle so no
// data is lost between observer callbacks.
const allScripts = [];
const observer = new PerformanceObserver(list => {
// Collect all script entries across frames to find the biggest offenders.
allScripts.push(...list.getEntries().flatMap(entry => entry.scripts));
// Group by sourceURL so you can identify which scripts contribute
// the most total time, even if each individual invocation is short.
const scriptSource = [...new Set(allScripts.map(script => script.sourceURL))];
const scriptsBySource = scriptSource.map(sourceURL => ([sourceURL,
allScripts.filter(script => script.sourceURL === sourceURL)
]));
const processedScripts = scriptsBySource.map(([sourceURL, scripts]) => ({
sourceURL,
count: scripts.length,
totalDuration: scripts.reduce((subtotal, script) => subtotal + script.duration, 0)
}));
// Only include scripts above a certain threshold to reduce noise.
const heavyScripts = processedScripts.filter(script => {
return script.totalDuration > 100;
});
// Sort by total duration so the worst offenders appear first,
// making it easier to prioritize optimization efforts.
heavyScripts.sort((a, b) => b.totalDuration - a.totalDuration);
// Log to the console for local debugging. In production, replace
// this with a call to send the data to your analytics service.
console.table(heavyScripts);
});
// Use buffered: true to capture any long frames that occurred before
// this observer was registered.
observer.observe({type: 'long-animation-frame', buffered: true});
แนวทางปฏิบัติแนะนำ
- ควรใช้ Long Animation Frames API มากกว่าทางเลือกอื่นๆ เช่น JS Self-Profiling API ซึ่งมีค่าใช้จ่ายในการรันไทม์สูงกว่า
- ควรสรุปข้อมูลสำคัญเนื่องจาก Long Animation Frames API มีรายละเอียดจำนวนมาก
- ควรส่งข้อมูลที่จำเป็นไปยังบริการวิเคราะห์ในเวอร์ชันที่ใช้งานจริง
การรองรับเบราว์เซอร์และกลยุทธ์สำรอง
เบราว์เซอร์ที่ไม่รองรับ Long Animation Frames API จะละเว้น API นี้ จึงสามารถใช้งานได้อย่างปลอดภัยโดยไม่ต้องมีกลไกสำรอง ในกรณีส่วนใหญ่ โอกาสในการปรับปรุงประสิทธิภาพที่ระบุจะใช้กับเบราว์เซอร์อื่นๆ ได้ด้วย