ระบุสาเหตุที่ทำให้ INP ไม่ดี

การตอบสนองต่อการโต้ตอบที่ไม่ดีทำให้เกิดความประทับใจที่ไม่ดีว่าหน้าเว็บช้าหรือถึงขั้นใช้งานไม่ได้เลย Interaction to Next Paint (INP) เป็นเมตริกที่อิงตาม Event Timing API โดยจะวัดการโต้ตอบที่แย่ที่สุด (ไม่รวมค่าผิดปกติบางค่า) เพื่อวัดการตอบสนองของหน้าเว็บ

การระบุสาเหตุของปัญหาหน้าเว็บไม่ตอบสนองอาจเป็นเรื่องยาก โดยเฉพาะอย่างยิ่งเมื่อขึ้นอยู่กับการโต้ตอบของผู้ใช้และสภาพแวดล้อม เช่น ความสามารถของอุปกรณ์และสภาพเครือข่าย ซึ่งทำให้วิเคราะห์ได้ยากกว่าเมื่อเทียบกับสถานการณ์ที่ทำซ้ำและคาดการณ์ได้มากกว่า เช่น การโหลดหน้าเว็บ ข้อมูลจากห้องปฏิบัติการจะจำลองสถานการณ์ของผู้ใช้จริงเพียงส่วนเล็กๆ เท่านั้น ดังนั้นการวัดสาเหตุที่ทำให้ INP ช้าในภาคสนามจึงเป็นสิ่งสำคัญ

Event Timing API ช่วยให้สามารถแบ่งระยะเวลา INP ออกเป็น 3 ส่วนย่อย ได้แก่ ความล่าช้าของอินพุต (การประมวลผลที่เกิดขึ้นแล้วเมื่อเกิดการโต้ตอบ) ระยะเวลาการประมวลผล (ความล่าช้าอันเป็นผลโดยตรงจากการโต้ตอบ) และความล่าช้าในการนำเสนอ (ความล่าช้าที่เกิดจากการแสดงผลเฟรมถัดไปหลังจากการโต้ตอบ) ซึ่งจะช่วยระบุว่าปัญหาอยู่ในโค้ดอื่นๆ, โค้ด JavaScript ของการโต้ตอบ หรือการอัปเดตการแสดงผลการประมวลผลของเบราว์เซอร์ตามลำดับ แทนที่จะข้ามไปยังโค้ดการโต้ตอบโดยตรง

คุณสามารถรวบรวมข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับ JavaScript, การคำนวณสไตล์ใหม่ และงานเลย์เอาต์ที่ทำให้การโต้ตอบล่าช้า การติดตามประสิทธิภาพแบบเต็มรูปแบบโดยใช้ JS Self-Profiling API เป็นโซลูชันที่ใช้ทรัพยากรมากซึ่งอาจทำให้เกิดปัญหาด้านประสิทธิภาพ Long Animation Frames API เป็น API ที่มีขนาดเล็กซึ่งสามารถใช้ในภาคสนามเพื่อระบุ JavaScript ที่ทำงานช้า และแยกความแตกต่างระหว่างการคำนวณสไตล์ใหม่และต้นทุนเลย์เอาต์ระหว่างการโต้ตอบ INP

  • สไตล์และเลย์เอาต์ที่บังคับใช้ในระดับสคริปต์ (forcedStyleDuration และ forcedLayoutDuration): รายงานการคำนวณสไตล์ใหม่แบบซิงโครนัส (เช่น การเรียกใช้ getComputedStyle() หลังจากการเปลี่ยนแปลง DOM) และเลย์เอาต์/การจัดเรียงใหม่ที่บังคับใช้ (เช่น การอ่าน getBoundingClientRect() หรือ offsetWidth หลังจากการเปลี่ยนแปลง) ภายในสคริปต์ที่ทำงานอยู่ระหว่างความล่าช้าของอินพุตหรือระยะเวลาการประมวลผล
  • สไตล์และเลย์เอาต์การแสดงผลระดับเฟรม (styleDuration และ layoutDuration): รายงานเวลาที่ใช้ในการคำนวณสไตล์ใหม่เทียบกับการอัปเดตเลย์เอาต์ระหว่างการอัปเดตการแสดงผลของเฟรม ซึ่งช่วยอธิบายความล่าช้าในการนำเสนอที่สูง

การแยกความแตกต่างระหว่างการคํานวณสไตล์ใหม่กับเลย์เอาต์เป็นสิ่งสําคัญเนื่องจากมีสาเหตุของปัญหาและการแก้ไขที่แตกต่างกัน กล่าวคือ ระยะเวลาการคํานวณสไตล์ที่สูงบ่งชี้ถึงการจับคู่ตัวเลือก CSS ที่มีค่าใช้จ่ายสูงหรือการทำให้สไตล์ไม่ถูกต้องในวงกว้าง (แก้ไขโดยการลดความซับซ้อนของตัวเลือกหรือลดขอบเขตของกฎ) ในขณะที่ระยะเวลาการจัดเลย์เอาต์ที่สูงบ่งชี้ถึงการคํานวณเรขาคณิตที่มีค่าใช้จ่ายสูงหรือการจัดเลย์เอาต์แบบซิงโครนัสที่ไม่มีประสิทธิภาพ (แก้ไขโดยการจัดกลุ่มการอ่าน DOM ก่อนการเขียน ลดความซับซ้อนของโครงสร้าง DOM หรือใช้การกักกัน CSS)

วิธีการนำไปใช้งาน

การคำนวณ INP จาก Event Timing API แบบข้อมูลดิบนั้นซับซ้อนและมีรายละเอียดปลีกย่อยหลายอย่าง เราขอแนะนำให้คุณใช้เครื่องมือหรือไลบรารี RUM เพื่อรวบรวมข้อมูลนี้

web-vitals เป็นไลบรารีโอเพนซอร์สจาก Google ที่คำนวณ Core Web Vitals รวมถึง INP และยังมีส่วนย่อยและข้อมูลเฟรมของภาพเคลื่อนไหวที่ใช้เวลานานสำหรับการโต้ตอบ INP ด้วย

รับการระบุแหล่งที่มาสำหรับการโต้ตอบ INP โดยใช้ไลบรารี web-vitals

ไลบรารี web-vitals เป็นไลบรารีขนาดเล็กที่ใช้ในการวัด Core Web Vitals และเมตริกประสิทธิภาพอื่นๆ คุณสามารถใช้ฟังก์ชัน onINP() เพื่อระบุการโต้ตอบที่ช้าที่สุด และรวมข้อมูลเกี่ยวกับส่วนย่อยของ INP, สคริปต์ที่ดำเนินการระหว่างการโต้ตอบ รวมถึงระยะเวลาของสไตล์และเลย์เอาต์โดยใช้ Long Animation Frames API

// Use the attribution build to get Long Animation Frame data
// alongside the INP metric value.
import { onINP } from 'web-vitals/attribution';

onINP((metric) => {
  const { attribution } = metric;
  const longestScript = attribution.longestScript;

  // Sum rendering-phase style and layout durations across all LoAF entries
  // intersecting the INP interaction (primarily explains presentationDelay).
  let renderStyleDuration = 0;
  let renderLayoutDuration = 0;
  for (const entry of attribution.longAnimationFrameEntries) {
    renderStyleDuration += entry.styleDuration ?? 0;
    renderLayoutDuration += entry.layoutDuration ?? 0;
  }

  // Beacon script, style, and layout attribution for the INP interaction,
  // so you can identify the root cause in production.
  navigator.sendBeacon(
    '/analytics',
    JSON.stringify({
      name: 'INP',
      value: metric.value,
      // These fields give the INP subparts:
      inputDelay: attribution.inputDelay,
      presentationDelay: attribution.presentationDelay,
      processingDuration: attribution.processingDuration,
      interactionTarget: attribution.interactionTarget,
      // These fields identify which script function was responsible
      // for the longest processing during the INP interaction.
      invokerType: longestScript?.entry?.invokerType,
      sourceURL: longestScript?.entry?.sourceURL,
      sourceFunctionName: longestScript?.entry?.sourceFunctionName,
      sourceCharPosition: longestScript?.entry?.sourceCharPosition,
      // subpart indicates which phase (input delay, processing, or
      // presentation delay) the longest script overlapped with most.
      subpart: longestScript?.subpart,
      intersectingDuration: longestScript?.intersectingDuration,
      // Synchronous style and layout work forced inside the longest script
      // (with forcedStyleAndLayoutDuration as a combined fallback):
      forcedStyleDuration: longestScript?.entry?.forcedStyleDuration,
      forcedLayoutDuration: longestScript?.entry?.forcedLayoutDuration,
      forcedStyleAndLayoutDuration: longestScript?.entry?.forcedStyleAndLayoutDuration,
      // Rendering-phase style and layout work across the interaction's frames
      // (with totalStyleAndLayoutDuration as a combined fallback):
      renderStyleDuration,
      renderLayoutDuration,
      totalStyleAndLayoutDuration: attribution.totalStyleAndLayoutDuration
    })
  );
});

แนวทางปฏิบัติแนะนำ

  • ควรใช้ส่วนย่อยของ INP ในตอนแรกเพื่อระบุว่าความล่าช้าเกิดจาก JavaScript ที่ทำงานอยู่แล้ว (ความล่าช้าของอินพุต) JavaScript ของตัวแฮนเดิลเหตุการณ์สำหรับการโต้ตอบ (ระยะเวลาการประมวลผล) หรือการแสดงผลที่ตามมา (ความล่าช้าในการนำเสนอ)
  • ควรพยายามระบุ JavaScript ที่บล็อกมากที่สุดเป็นรายละเอียดเพิ่มเติม โดยเฉพาะอย่างยิ่งสำหรับความล่าช้าของอินพุตและระยะเวลาการประมวลผล
  • ควรแยกความแตกต่างระหว่างการคำนวณสไตล์ใหม่ (styleDuration, forcedStyleDuration) และเลย์เอาต์ (layoutDuration, forcedLayoutDuration) เมื่อวิเคราะห์ระยะเวลาการประมวลผลที่สูงหรือความล่าช้าในการนำเสนอ โดยเวลาในการคำนวณสไตล์ที่สูงบ่งชี้ถึงตัวเลือก CSS ที่มีค่าใช้จ่ายสูงหรือการทำให้เป็นโมฆะในวงกว้าง ส่วนเวลาในการจัดเลย์เอาต์ที่สูงบ่งชี้ถึงคอขวดของเรขาคณิต DOM หรือการจัดเลย์เอาต์แบบซิงโครนัสที่ไม่มีประสิทธิภาพ
  • ควรใช้ Long Animation Frames API เพื่อให้รายละเอียดเพิ่มเติมนี้แทนทางเลือกอื่นๆ เช่น JS Self-Profiling API ซึ่งมีค่าใช้จ่ายเพิ่มเติมเกี่ยวกับรันไทม์ที่สูงกว่า
  • ควรใช้ไลบรารี web-vitals หากไม่มีโซลูชัน RUM อื่นๆ โดยสามารถระบุการโต้ตอบ INP และมีข้อมูลเกี่ยวกับส่วนย่อยและสคริปต์ที่ดำเนินการระหว่างการโต้ตอบ (โดยใช้ Long Animation Frames API)
  • ควรส่งข้อมูลที่จำเป็นกลับไปยังบริการวิเคราะห์แทนที่จะบันทึกไว้ในเครื่องเท่านั้น

การรองรับเบราว์เซอร์และกลยุทธ์สำรอง

Event Timing API พร้อมใช้งานในเบราว์เซอร์ที่ทันสมัยส่วนใหญ่ และจำเป็นสำหรับการคำนวณ INP และส่วนย่อยของ INP สำหรับเบราว์เซอร์ที่ไม่รองรับ API นี้ ระบบจะวัด INP ไม่ได้

Long Animation Frames API จะให้รายละเอียดเพิ่มเติม (ไม่บังคับ) คุณจึงใช้ได้อย่างปลอดภัยโดยไม่ต้องมีโฆษณาสำรอง ในกรณีส่วนใหญ่ โอกาสในการปรับปรุงประสิทธิภาพที่ระบุจะใช้กับเบราว์เซอร์อื่นๆ ได้ด้วย

หากเป้าหมายพื้นฐานรองรับ Long Animation Frames API (long-animation-frames) แต่ไม่รองรับพร็อพเพอร์ตี้ระยะเวลาของสไตล์และเลย์เอาต์แยกกัน (styleDuration และ layoutDuration ใน PerformanceLongAnimationFrameTiming หรือ forcedStyleDuration และ forcedLayoutDuration ใน PerformanceScriptTiming) ให้เปลี่ยนไปใช้เวลาของสไตล์และเลย์เอาต์รวมกัน ดังนี้

  • ใช้ script.forcedStyleAndLayoutDuration ใน PerformanceScriptTiming สำหรับการทำงานของสไตล์และเลย์เอาต์ที่บังคับแบบซิงโครนัสภายในสคริปต์
  • ใช้ metric.attribution.totalStyleAndLayoutDuration ใน web-vitals/attribution (หรือคำนวณ (entry.paintTime || entry.startTime + entry.duration) - entry.styleAndLayoutStart ในแต่ละรายการ PerformanceLongAnimationFrameTiming) สำหรับงานสไตล์และเลย์เอาต์แบบผสม