ระบุ JavaScript ที่ทำงานหนัก

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 นี้ จึงสามารถใช้งานได้อย่างปลอดภัยโดยไม่ต้องมีกลไกสำรอง ในกรณีส่วนใหญ่ โอกาสในการปรับปรุงประสิทธิภาพที่ระบุจะใช้กับเบราว์เซอร์อื่นๆ ได้ด้วย