ค้นหาการโต้ตอบที่ช้าในช่อง

ดูวิธีค้นหาการโต้ตอบที่ช้าในข้อมูลฟิลด์ของเว็บไซต์ เพื่อค้นหาโอกาสในการปรับปรุงการโต้ตอบกับ Next Paint

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

ในคู่มือนี้ คุณจะได้เรียนรู้วิธีประเมิน INP ของเว็บไซต์อย่างรวดเร็วโดยใช้ข้อมูลภาคสนามจากรายงานประสบการณ์ของผู้ใช้ Chrome (CrUX) เพื่อดูว่าเว็บไซต์ของคุณมีปัญหาเกี่ยวกับ INP หรือไม่ หลังจากนั้น คุณจะได้ทราบวิธีใช้บิลด์การระบุแหล่งที่มาของไลบรารี JavaScript ของประเภทการใช้เว็บ และข้อมูลเชิงลึกใหม่ที่ได้จาก Long Animation Frames API (LoAF) เพื่อรวบรวมและตีความข้อมูลภาคสนามสําหรับการโต้ตอบที่ช้าในเว็บไซต์

เริ่มต้นด้วย CrUX เพื่อประเมิน INP ของเว็บไซต์

หากคุณไม่ได้เก็บรวบรวมข้อมูลฟิลด์จากผู้ใช้เว็บไซต์ CrUX อาจเป็นจุดเริ่มต้นที่ดี CrUX รวบรวมข้อมูลภาคสนามจากผู้ใช้ Chrome จริงที่เลือกรับการส่งข้อมูลทางไกล

ข้อมูล CrUX จะปรากฏในพื้นที่ต่างๆ หลายแห่ง และขึ้นอยู่กับขอบเขตของข้อมูลที่คุณกำลังมองหา CrUX สามารถให้ข้อมูลเกี่ยวกับ INP และ Core Web Vitals อื่นๆ สำหรับ

  • หน้าเว็บแต่ละหน้าและต้นทางทั้งหมดโดยใช้ PageSpeed Insights
  • ประเภทของหน้าเว็บ ตัวอย่างเช่น เว็บไซต์อีคอมเมิร์ซหลายแห่งมีประเภทหน้ารายละเอียดผลิตภัณฑ์และข้อมูลผลิตภัณฑ์ที่แสดง คุณดูข้อมูล CrUX สำหรับประเภทหน้าที่ไม่ซ้ำกันได้ใน Search Console

คุณอาจเริ่มต้นด้วยการป้อน URL ของเว็บไซต์ใน PageSpeed Insights เมื่อคุณป้อน URL ข้อมูลช่องสำหรับ URL นั้น (หากมี) จะแสดงสำหรับหลายเมตริก รวมถึง INP คุณยังใช้ปุ่มสลับเพื่อตรวจสอบค่า INP สำหรับมิติข้อมูลอุปกรณ์เคลื่อนที่และเดสก์ท็อปได้ด้วย

วันที่ ข้อมูลช่องที่ CrUX แสดงใน PageSpeed Insights แสดง LCP, INP, CLS ที่ Core Web Vitals ทั้ง 3 รายการ และ TTFB, FCP เป็นเมตริกการวินิจฉัย และ FID เป็นเมตริก Core Web Vitals ที่เลิกใช้งานแล้ว
การอ่านข้อมูล CrUX ตามที่เห็นใน PageSpeed Insights ในตัวอย่างนี้ INP ของหน้าเว็บต้องปรับปรุง

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

เก็บรวบรวมข้อมูลช่องด้วยไลบรารี JavaScript ของ web-vitals

ไลบรารี JavaScript web-vitals คือสคริปต์ที่โหลดบนเว็บไซต์เพื่อรวบรวมข้อมูลช่องจากผู้ใช้เว็บไซต์ คุณสามารถใช้เพื่อบันทึกเมตริกต่างๆ มากมาย รวมถึง INP ในเบราว์เซอร์ที่รองรับ

การรองรับเบราว์เซอร์

  • Chrome: 96.
  • ขอบ: 96
  • Firefox: ไม่สนับสนุน
  • Safari: ไม่รองรับ

แหล่งที่มา

ไลบรารีไฟล์วิตามินบนเว็บเวอร์ชันมาตรฐานสามารถใช้เพื่อรับข้อมูล INP พื้นฐานจากผู้ใช้ในภาคสนามได้ ดังนี้

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  console.log(name);    // 'INP'
  console.log(value);   // 512
  console.log(rating);  // 'poor'
});

ในการวิเคราะห์ข้อมูลในช่องจากผู้ใช้ คุณจะต้องส่งข้อมูลนี้ไปที่ใดที่หนึ่ง

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  // Prepare JSON to be sent for collection. Note that
  // you can add anything else you'd want to collect here:
  const body = JSON.stringify({name, value, rating});

  // Use `sendBeacon` to send data to an analytics endpoint.
  // For Google Analytics, see https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics.
  navigator.sendBeacon('/analytics', body);
});

อย่างไรก็ตาม ข้อมูลนี้ไม่ได้บอกอะไรคุณมากกว่าที่ CrUX มี นี่แหละคือจุดประสงค์ของการสร้างการระบุแหล่งที่มาของไลบรารีวิตามินจากเว็บ

ไปได้ไกลขึ้นด้วยการสร้างรูปแบบการระบุแหล่งที่มาของไลบรารีเว็บไวรัล

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

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, rating, attribution}) => {
  console.log(name);         // 'INP'
  console.log(value);        // 56
  console.log(rating);       // 'good'
  console.log(attribution);  // Attribution data object
});
ลักษณะที่บันทึกของคอนโซลจากไลบรารีไฟล์วิดีโอบนเว็บจะปรากฏขึ้น คอนโซลในตัวอย่างนี้แสดงชื่อเมตริก (INP), ค่า INP (56) โดยที่ค่านั้นอยู่ภายในเกณฑ์ INP (ดี) และบิตข้อมูลต่างๆ ที่แสดงในออบเจ็กต์การระบุแหล่งที่มา รวมถึงรายการจาก Long Animation Frames API
ข้อมูลที่จากไลบรารีไฟล์วิดีโอบนเว็บจะปรากฏในคอนโซลอย่างไร

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

  • "ผู้ใช้ได้โต้ตอบกับหน้าเว็บขณะที่ระบบกำลังโหลดหรือไม่"
  • "เครื่องจัดการเหตุการณ์ของการโต้ตอบทำงานเป็นเวลานานหรือไม่"
  • "โค้ดเครื่องจัดการเหตุการณ์การโต้ตอบล่าช้าตั้งแต่เริ่มต้นหรือไม่ หากใช่ มีอะไรเกิดขึ้นอีกในชุดข้อความหลักในเวลานั้น"
  • "การโต้ตอบทำให้เกิดการแสดงผลจำนวนมากซึ่งทำให้การแสดงผลเฟรมถัดไปล่าช้าหรือไม่"

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

คีย์ออบเจ็กต์ attribution ข้อมูล
interactionTarget ตัวเลือก CSS ที่ชี้ไปยังองค์ประกอบที่สร้างค่า INP ของหน้าเว็บ เช่น button#save
interactionType ประเภทของการโต้ตอบซึ่งมาจากการคลิก การแตะ หรือการป้อนข้อมูลด้วยแป้นพิมพ์
inputDelay* การหน่วงเวลาอินพุตของการโต้ตอบ
processingDuration* เวลาตั้งแต่ที่ Listener เหตุการณ์แรกเริ่มทำงานเพื่อตอบสนองการโต้ตอบของผู้ใช้จนกระทั่งการประมวลผล Listener เหตุการณ์ทั้งหมดเสร็จสิ้น
presentationDelay* การหน่วงเวลาการนำเสนอของการโต้ตอบ ซึ่งเกิดขึ้นโดยเริ่มตั้งแต่เวลาที่เครื่องจัดการเหตุการณ์เสร็จสิ้นจนถึงเวลาที่แสดงผลเฟรมถัดไป
longAnimationFrameEntries* รายการจาก LoAF ที่เชื่อมโยงกับการโต้ตอบ โปรดดูข้อมูลเพิ่มเติมในส่วนถัดไป
*มาใหม่ในเวอร์ชัน 4

ตั้งแต่ไลบรารี Web-vitals เวอร์ชัน 4 คุณสามารถรับข้อมูลเชิงลึกที่ละเอียดยิ่งขึ้นเกี่ยวกับการโต้ตอบที่เป็นปัญหาผ่านข้อมูลที่ได้จากรายละเอียดเฟส INP (ความล่าช้าของอินพุต ระยะเวลาการประมวลผล และความล่าช้าของการนำเสนอ) และ Long Animation Frames API (LoAF)

Long Animation Frames API (LoAF)

การรองรับเบราว์เซอร์

  • Chrome: 123
  • ขอบ: 123
  • Firefox: ไม่สนับสนุน
  • Safari: ไม่รองรับ

แหล่งที่มา

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

บิลด์การระบุแหล่งที่มาของไลบรารี Web-vitals จะแสดงอาร์เรย์ของรายการ LoAF ใต้คีย์ longAnimationFrameEntries ของออบเจ็กต์ attribution ตารางต่อไปนี้แสดงรายการข้อมูลที่สำคัญบางส่วนที่คุณจะพบในรายการ LoAF แต่ละรายการ

คีย์ออบเจ็กต์รายการ LoAF ข้อมูล
duration ระยะเวลาของเฟรมภาพเคลื่อนไหวยาวจนถึงเมื่อจัดเลย์เอาต์เสร็จสิ้น แต่ไม่รวมการลงสีและการประกอบรูปภาพ
blockingDuration ระยะเวลาทั้งหมดในเฟรมที่เบราว์เซอร์ไม่สามารถตอบสนองได้อย่างรวดเร็วเนื่องจากมีงานที่ใช้เวลานาน เวลาในการบล็อกนี้อาจรวมถึงงานที่ใช้เวลานานซึ่งเรียกใช้ JavaScript รวมถึงงานการแสดงผลระยะยาวใดๆ ที่ตามมาในเฟรม
firstUIEventTimestamp การประทับเวลาที่มีการจัดคิวเหตุการณ์ระหว่างการจัดเฟรม มีประโยชน์ในการหาจุดเริ่มต้นของความล่าช้าในการป้อนข้อมูลของการโต้ตอบ
startTime การประทับเวลาเริ่มต้นของเฟรม
renderStart เวลาที่การแสดงผลสำหรับเฟรมเริ่มต้นขึ้น ซึ่งรวมถึง Callback ของ requestAnimationFrame (และ Callback ResizeObserver หากมี) แต่อาจก่อนที่งานสไตล์/เลย์เอาต์ใดๆ จะเริ่มต้น
styleAndLayoutStart เมื่อเกิดการจัดรูปแบบ/เลย์เอาต์ในเฟรม มีประโยชน์ในการหาความยาวของสไตล์/เลย์เอาต์เมื่อพิจารณาการประทับเวลาอื่นๆ ที่มี
scripts อาร์เรย์ของรายการที่มีข้อมูลการระบุแหล่งที่มาของสคริปต์ที่มีส่วนใน INP ของหน้าเว็บ
วันที่ การแสดงภาพเฟรมภาพเคลื่อนไหวขนาดยาวตามโมเดล LoAF
แผนภาพแสดงระยะเวลาของเฟรมภาพเคลื่อนไหวขนาดยาวตาม LoAF API (ลบ blockingDuration)

ข้อมูลทั้งหมดนี้สามารถบอกได้มากเกี่ยวกับสาเหตุที่ทำให้การโต้ตอบช้าลง แต่อาร์เรย์ scripts ที่รายการ LoAF แสดงควรน่าสนใจเป็นพิเศษ

คีย์ออบเจ็กต์การระบุแหล่งที่มาของสคริปต์ ข้อมูล
invoker ผู้เรียกใช้ ซึ่งอาจแตกต่างกันไปตามประเภทผู้เรียกใช้ที่อธิบายไว้ในแถวถัดไป ตัวอย่างของผู้เรียกใช้อาจเป็นค่าต่างๆ เช่น 'IMG#id.onload', 'Window.requestAnimationFrame' หรือ 'Response.json.then'
invokerType ประเภทของผู้เรียกใช้ ซึ่งอาจเป็น 'user-callback', 'event-listener', 'resolve-promise', 'reject-promise', 'classic-script' หรือ 'module-script'
sourceURL URL ไปยังสคริปต์ซึ่งเป็นที่มาของเฟรมภาพเคลื่อนไหวขนาดยาว
sourceCharPosition ตำแหน่งอักขระในสคริปต์ที่ระบุโดย sourceURL
sourceFunctionName ชื่อของฟังก์ชันในสคริปต์ที่ระบุ

แต่ละรายการในอาร์เรย์นี้จะมีข้อมูลที่แสดงในตารางนี้ ซึ่งให้ข้อมูลเกี่ยวกับสคริปต์ที่ทําให้การโต้ตอบช้า รวมถึงลักษณะการทํางาน

วัดและระบุสาเหตุที่พบบ่อยของการมีการโต้ตอบช้า

เพื่อให้คุณเห็นภาพว่าคุณจะใช้ข้อมูลนี้อย่างไร ต่อไป คู่มือนี้จะอธิบายวิธีใช้ข้อมูล LoAF ที่ปรากฏในไลบรารี web-vitals เพื่อหาสาเหตุบางอย่างที่ทำให้การโต้ตอบช้า

ระยะเวลาในการประมวลผลที่นาน

ระยะเวลาการประมวลผลของการโต้ตอบคือระยะเวลาที่ Callback ของตัวแฮนเดิลเหตุการณ์ที่ลงทะเบียนไว้ของการโต้ตอบทำงานเพื่อทำงานให้เสร็จสมบูรณ์และสิ่งอื่นๆ ที่อาจเกิดขึ้นระหว่างการโต้ตอบ ไลบรารีไฟล์วิดีโอบนเว็บจะใช้เวลาในการประมวลผลสูง:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5
});

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

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5

  // Get the longest script from LoAF covering `processingDuration`:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Get attribution for the long-running event handler:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

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

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

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

ความล่าช้าของอินพุตยาว

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

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536
});

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

เกิดปัญหาระหว่างการโหลดหน้าเว็บใช่ไหม

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

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Invoker types can describe if script eval blocked the main thread:
    const {invokerType} = script;    // 'classic-script' | 'module-script'
    const {sourceLocation} = script; // 'https://example.com/app.js'
  }
});

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

ที่เกิดขึ้นหลังจากที่หน้าเว็บโหลดหรือไม่

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

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    const {invokerType} = script;        // 'user-callback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

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

  • 'user-callback' บ่งบอกว่างานบล็อกมาจาก setInterval, setTimeout หรือแม้แต่ requestAnimationFrame
  • 'event-listener' บ่งบอกว่างานการบล็อกมาจากอินพุตก่อนหน้านี้ซึ่งจัดคิวไว้และยังคงประมวลผลอยู่
  • 'resolve-promise' และ 'reject-promise' หมายความว่างานการบล็อกมาจากงานที่ไม่พร้อมกันซึ่งเริ่มทำก่อนหน้านี้ แต่มีการแก้ไขหรือปฏิเสธในเวลาที่ผู้ใช้พยายามโต้ตอบกับหน้าเว็บ ทำให้การโต้ตอบล่าช้าลง

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

การนำเสนอล่าช้ามาก

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

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691
});

หากคุณบันทึกข้อมูลนี้และพบว่ามีความล่าช้าในการนำเสนอสูงสำหรับการโต้ตอบที่มีผลต่อ INP ของเว็บไซต์ สาเหตุอาจแตกต่างกันไป แต่ต่อไปนี้คือสาเหตุ 2 ประการที่ควรเฝ้าระวัง

งานสไตล์และเลย์เอาต์ที่มีราคาแพง

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

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get necessary timings:
  const {startTime} = loaf; // 2120.5
  const {duration} = loaf;  // 1002

  // Figure out the ending timestamp of the frame (approximate):
  const endTime = startTime + duration; // 3122.5

  // Get the start timestamp of the frame's style/layout work:
  const {styleAndLayoutStart} = loaf; // 3011.17692309

  // Calculate the total style/layout duration:
  const styleLayoutDuration = endTime - styleAndLayoutStart; // 111.32307691

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running style and layout operation:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

LoAF จะไม่บอกระยะเวลาของงานสไตล์และเลย์เอาต์ของเฟรม แต่จะบอกให้คุณทราบเมื่อเริ่มใช้งาน ด้วยการประทับเวลาเริ่มต้นนี้ คุณสามารถใช้ข้อมูลอื่นๆ จาก LoAF เพื่อคำนวณระยะเวลาที่แม่นยำของงานนั้นๆ ด้วยการกำหนดเวลาสิ้นสุดของเฟรม แล้วลบการประทับเวลาเริ่มต้นของงานสไตล์และเลย์เอาต์จากช่วงเวลานั้น

Callback requestAnimationFrame รายการที่ยาวนาน

สาเหตุหนึ่งที่อาจทำให้นำเสนอล่าช้าคืองานมากเกินไปใน Callback ของ requestAnimationFrame เนื้อหาของการเรียกกลับนี้จะทำงานหลังจากตัวแฮนเดิลเหตุการณ์ทำงานเสร็จแล้ว แต่ก่อนที่จะมีการคำนวณรูปแบบใหม่และการทำงานเลย์เอาต์

Callback เหล่านี้อาจใช้เวลาดำเนินการค่อนข้างนานหากการดำเนินการในนั้นมีความซับซ้อน หากสงสัยว่าการหน่วงเวลาการนำเสนอที่สูงนั้นเกิดจากงานที่ทำกับ requestAnimationFrame ให้ใช้ข้อมูล LoAF ที่แสดงโดยไลบรารีไฟล์วิดีโอบนเว็บเพื่อระบุสถานการณ์ต่อไปนี้

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 543.1999999880791

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get the render start time and when style and layout began:
  const {renderStart} = loaf;         // 2489
  const {styleAndLayoutStart} = loaf; // 2989.5999999940395

  // Calculate the `requestAnimationFrame` callback's duration:
  const rafDuration = styleAndLayoutStart - renderStart; // 500.59999999403954

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running requestAnimationFrame callback:
    const {invokerType} = script;        // 'user-callback'
    const {invoker} = script;            // 'FrameRequestCallback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

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

บทสรุป

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

รูปภาพหลักจาก Unsplash โดย Federico Respini