ดูวิธีค้นหาการโต้ตอบที่ช้าในข้อมูลฟิลด์ของเว็บไซต์ เพื่อค้นหาโอกาสในการปรับปรุงการโต้ตอบกับ 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 ที่เลิกใช้งานแล้ว](https://web.developers.google.cn/static/articles/find-slow-interactions-in-the-field/image/fig-1.png?authuser=5&hl=th)
ข้อมูลนี้เป็นประโยชน์เพราะจะแจ้งให้คุณทราบได้หากคุณมีปัญหา อย่างไรก็ตาม สิ่งที่ CrUX ทำไม่ได้คือจะบอกคุณว่าอะไรที่ทำให้เกิดปัญหา มีโซลูชันการตรวจสอบผู้ใช้จริง (RUM) มากมายที่จะช่วยให้คุณรวบรวมข้อมูลภาคสนามจากผู้ใช้ของเว็บไซต์เพื่อช่วยในการตอบคำถามดังกล่าว และตัวเลือกหนึ่งคือการรวบรวมข้อมูลช่องนั้นด้วยตนเองโดยใช้ไลบรารี JavaScript สำหรับการอัปโหลดบนเว็บ
เก็บรวบรวมข้อมูลช่องด้วยไลบรารี JavaScript ของ web-vitals
ไลบรารี JavaScript web-vitals
คือสคริปต์ที่โหลดบนเว็บไซต์เพื่อรวบรวมข้อมูลช่องจากผู้ใช้เว็บไซต์ คุณสามารถใช้เพื่อบันทึกเมตริกต่างๆ มากมาย รวมถึง INP ในเบราว์เซอร์ที่รองรับ
ไลบรารีไฟล์วิตามินบนเว็บเวอร์ชันมาตรฐานสามารถใช้เพื่อรับข้อมูล 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](https://web.developers.google.cn/static/articles/find-slow-interactions-in-the-field/image/fig-2.png?authuser=5&hl=th)
นอกจาก INP ของหน้าเว็บแล้ว รูปแบบการระบุแหล่งที่มายังมีข้อมูลจำนวนมากที่คุณสามารถใช้เพื่อช่วยให้เข้าใจสาเหตุของการโต้ตอบช้า รวมถึงการโต้ตอบส่วนใดที่คุณควรมุ่งเน้น ซึ่งจะช่วยตอบคําถามสําคัญ เช่น
- "ผู้ใช้ได้โต้ตอบกับหน้าเว็บขณะที่ระบบกำลังโหลดหรือไม่"
- "เครื่องจัดการเหตุการณ์ของการโต้ตอบทำงานเป็นเวลานานหรือไม่"
- "โค้ดเครื่องจัดการเหตุการณ์การโต้ตอบล่าช้าตั้งแต่เริ่มต้นหรือไม่ หากใช่ มีอะไรเกิดขึ้นอีกในชุดข้อความหลักในเวลานั้น"
- "การโต้ตอบทำให้เกิดการแสดงผลจำนวนมากซึ่งทำให้การแสดงผลเฟรมถัดไปล่าช้าหรือไม่"
ตารางต่อไปนี้แสดงข้อมูลการระบุแหล่งที่มาพื้นฐานบางส่วนที่คุณจะได้รับจากไลบรารีซึ่งจะช่วยหาสาเหตุระดับสูงบางอย่างของการโต้ตอบช้าในเว็บไซต์
คีย์ออบเจ็กต์ attribution
|
ข้อมูล |
---|---|
interactionTarget
|
ตัวเลือก CSS ที่ชี้ไปยังองค์ประกอบที่สร้างค่า INP ของหน้าเว็บ เช่น button#save
|
interactionType
|
ประเภทของการโต้ตอบซึ่งมาจากการคลิก การแตะ หรือการป้อนข้อมูลด้วยแป้นพิมพ์ |
inputDelay *
|
การหน่วงเวลาอินพุตของการโต้ตอบ |
processingDuration *
|
เวลาตั้งแต่ที่ Listener เหตุการณ์แรกเริ่มทำงานเพื่อตอบสนองการโต้ตอบของผู้ใช้จนถึงเมื่อการประมวลผล Listener เหตุการณ์ทั้งหมดเสร็จสิ้น |
presentationDelay *
|
การหน่วงเวลาการนำเสนอของการโต้ตอบ ซึ่งเกิดขึ้นโดยเริ่มตั้งแต่เวลาที่เครื่องจัดการเหตุการณ์เสร็จสิ้นจนถึงเวลาที่แสดงผลเฟรมถัดไป |
longAnimationFrameEntries *
|
รายการจาก LoAF ที่เชื่อมโยงกับการโต้ตอบ โปรดดูข้อมูลเพิ่มเติมในส่วนถัดไป |
ตั้งแต่ไลบรารี Web-vitals เวอร์ชัน 4 คุณสามารถรับข้อมูลเชิงลึกที่ละเอียดยิ่งขึ้นเกี่ยวกับการโต้ตอบที่เป็นปัญหาผ่านข้อมูลที่ได้จากรายละเอียดเฟส INP (ความล่าช้าของอินพุต ระยะเวลาการประมวลผล และความล่าช้าของการนำเสนอ) และ Long Animation Frames API (LoAF)
Long Animation Frames API (LoAF)
การแก้ไขข้อบกพร่องของการโต้ตอบโดยใช้ข้อมูลภาคสนามเป็นงานที่ท้าทาย อย่างไรก็ตาม ข้อมูลจาก LoAF ทำให้ตอนนี้เราสามารถรับข้อมูลเชิงลึกที่ดีขึ้นเกี่ยวกับสาเหตุของการโต้ตอบช้า เนื่องจาก LoAF จะแสดงช่วงเวลาอย่างละเอียดและข้อมูลอื่นๆ ที่คุณสามารถใช้เพื่อระบุสาเหตุที่แม่นยำได้ และที่สำคัญกว่านั้นคือแหล่งที่มาของปัญหาอยู่ในโค้ดของเว็บไซต์คุณ
บิลด์การระบุแหล่งที่มาของไลบรารี Web-vitals จะแสดงอาร์เรย์ของรายการ LoAF ใต้คีย์ longAnimationFrameEntries
ของออบเจ็กต์ attribution
ตารางต่อไปนี้แสดงรายการข้อมูลที่สำคัญบางส่วนที่คุณจะพบในรายการ LoAF แต่ละรายการ
คีย์ออบเจ็กต์รายการ LoAF | ข้อมูล |
---|---|
duration
|
ระยะเวลาของเฟรมภาพเคลื่อนไหวยาวจนถึงเมื่อจัดเลย์เอาต์เสร็จสิ้น แต่ไม่รวมการลงสีและการประกอบรูปภาพ |
blockingDuration
|
ระยะเวลาทั้งหมดในเฟรมที่เบราว์เซอร์ไม่สามารถตอบสนองได้อย่างรวดเร็วเนื่องจากมีงานที่ใช้เวลานาน เวลาในการบล็อกนี้อาจรวมถึงงานที่ใช้เวลานานซึ่งเรียกใช้ JavaScript รวมถึงงานการแสดงผลระยะยาวใดๆ ที่ตามมาในเฟรม |
firstUIEventTimestamp
|
การประทับเวลาที่มีการจัดคิวเหตุการณ์ระหว่างการจัดเฟรม มีประโยชน์ในการหาจุดเริ่มต้นของความล่าช้าในการป้อนข้อมูลของการโต้ตอบ |
startTime
|
การประทับเวลาเริ่มต้นของเฟรม |
renderStart
|
เวลาที่การแสดงผลสำหรับเฟรมเริ่มต้นขึ้น ซึ่งรวมถึง Callback ของ requestAnimationFrame (และ Callback ResizeObserver หากมี) แต่อาจก่อนที่งานสไตล์/เลย์เอาต์ใดๆ จะเริ่มต้น
|
styleAndLayoutStart
|
เมื่อเกิดการจัดรูปแบบ/เลย์เอาต์ในเฟรม มีประโยชน์ในการหาความยาวของสไตล์/เลย์เอาต์เมื่อพิจารณาการประทับเวลาอื่นๆ ที่มี |
scripts
|
อาร์เรย์ของรายการที่มีข้อมูลการระบุแหล่งที่มาของสคริปต์ที่มีส่วนใน INP ของหน้าเว็บ |
![การแสดงภาพเฟรมภาพเคลื่อนไหวขนาดยาวตามโมเดล LoAF](https://web.developers.google.cn/static/articles/find-slow-interactions-in-the-field/image/fig-3.png?authuser=5&hl=th)
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