1. บทนำ
นี่คือ Codelab แบบอินเทอร์แอกทีฟสําหรับการเรียนรู้วิธีวัด Interaction to Next Paint (INP) โดยใช้ไลบรารี web-vitals
ข้อกำหนดเบื้องต้น
- ความรู้ด้านการพัฒนา HTML และ JavaScript
- แนะนํา: อ่านเอกสารประกอบเกี่ยวกับเมตริก INP ของ web.dev
สิ่งที่คุณจะได้เรียนรู้
- วิธีเพิ่มคลัง
web-vitals
ลงในหน้าเว็บและใช้ข้อมูลการระบุแหล่งที่มา - ใช้ข้อมูลการระบุแหล่งที่มาเพื่อวิเคราะห์ว่าควรเริ่มปรับปรุง INP ที่ไหนและอย่างไร
สิ่งที่ต้องมี
- คอมพิวเตอร์ที่สามารถโคลนโค้ดจาก GitHub และเรียกใช้คำสั่ง npm
- เครื่องมือแก้ไขข้อความ
- Chrome เวอร์ชันล่าสุดเพื่อให้การวัดการโต้ตอบทั้งหมดทํางานได้
2. ตั้งค่า
รับและเรียกใช้โค้ด
โค้ดจะอยู่ในที่เก็บ web-vitals-codelabs
- โคลนที่เก็บในเทอร์มินัล:
git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git
- ไปยังไดเรกทอรีที่โคลน:
cd web-vitals-codelabs/measuring-inp
- ติดตั้งการอ้างอิง:
npm ci
- เริ่มเว็บเซิร์ฟเวอร์:
npm run start
- ไปที่ http://localhost:8080/ ในเบราว์เซอร์
ลองใช้หน้าเว็บ
โค้ดแล็บนี้ใช้ Gastropodicon (เว็บไซต์อ้างอิงกายวิภาคหอยทากที่ได้รับความนิยม) เพื่อสำรวจปัญหาที่อาจเกิดขึ้นกับ INP
ลองโต้ตอบกับหน้าเว็บเพื่อดูว่าการโต้ตอบใดช้า
3. ทำความรู้จักกับเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome
เปิดเครื่องมือสำหรับนักพัฒนาเว็บจากเมนูเครื่องมือเพิ่มเติม > เครื่องมือสำหรับนักพัฒนาเว็บ โดยคลิกขวาที่หน้าเว็บแล้วเลือกตรวจสอบ หรือใช้แป้นพิมพ์ลัด
ในโค้ดแล็บนี้ เราจะใช้ทั้งแผงประสิทธิภาพและคอนโซล คุณสลับระหว่างแท็บเหล่านี้ในแท็บที่ด้านบนของเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ได้ทุกเมื่อ
- ปัญหา INP มักเกิดขึ้นในอุปกรณ์เคลื่อนที่ ดังนั้นให้เปลี่ยนไปใช้การจําลองการแสดงผลบนอุปกรณ์เคลื่อนที่
- หากคุณทดสอบบนเดสก์ท็อปหรือแล็ปท็อป ประสิทธิภาพมีแนวโน้มที่จะดีกว่าบนอุปกรณ์เคลื่อนที่จริงอย่างมาก หากต้องการดูประสิทธิภาพที่ใกล้เคียงกับความเป็นจริงมากขึ้น ให้กดไอคอนเฟืองที่ด้านขวาบนของแผงประสิทธิภาพ แล้วเลือกCPU ทำงานช้าลง 4 เท่า
4. การติดตั้ง web-vitals
web-vitals
เป็นไลบรารี JavaScript สําหรับวัดเมตริก Web Vitals ที่ผู้ใช้ได้รับ คุณสามารถใช้ไลบรารีเพื่อบันทึกค่าเหล่านั้น จากนั้นส่งบีคอนไปยังปลายทางข้อมูลวิเคราะห์เพื่อการวิเคราะห์ในภายหลัง เพื่อวัตถุประสงค์ในการระบุเวลาและตําแหน่งที่เกิดการโต้ตอบช้า
การเพิ่มคลังลงในหน้าเว็บทำได้หลายวิธี วิธีติดตั้งไลบรารีในเว็บไซต์ของคุณเองจะขึ้นอยู่กับวิธีจัดการทรัพยากร กระบวนการสร้าง และปัจจัยอื่นๆ โปรดอ่านเอกสารของคลังเพื่อดูตัวเลือกทั้งหมด
Codelab นี้จะติดตั้งจาก npm และโหลดสคริปต์โดยตรงเพื่อหลีกเลี่ยงการเจาะลึกกระบวนการสร้างที่เฉพาะเจาะจง
web-vitals
มี 2 เวอร์ชันที่คุณใช้ได้ ดังนี้
- คุณควรใช้บิลด์ "มาตรฐาน" หากต้องการติดตามค่าเมตริกของ Core Web Vitals ในการโหลดหน้าเว็บ
- บิลด์ "การระบุแหล่งที่มา" จะเพิ่มข้อมูลการแก้ไขข้อบกพร่องเพิ่มเติมลงในเมตริกแต่ละรายการเพื่อวิเคราะห์สาเหตุที่เมตริกมีค่าเป็นเช่นนั้น
สำหรับการวัด INP ในโค้ดแล็บนี้ เราต้องการบิลด์การระบุแหล่งที่มา
เพิ่ม web-vitals
ลงใน devDependencies
ของโปรเจ็กต์โดยเรียกใช้ npm install -D web-vitals
วิธีเพิ่ม web-vitals
ลงในหน้าเว็บ
เพิ่มสคริปต์เวอร์ชันการระบุแหล่งที่มาลงที่ด้านล่างของ index.html
และบันทึกผลลัพธ์ลงในคอนโซล
<script type="module">
import {onINP} from './node_modules/web-vitals/dist/web-vitals.attribution.js';
onINP(console.log);
</script>
ลองเลย
ลองโต้ตอบกับหน้าเว็บอีกครั้งโดยเปิดคอนโซลไว้ ขณะคลิกไปรอบๆ หน้าเว็บ ระบบจะไม่บันทึกข้อมูลใดๆ
INP จะวัดตลอดอายุการใช้งานของหน้าเว็บ ดังนั้นโดยค่าเริ่มต้น web-vitals
จะไม่รายงาน INP จนกว่าผู้ใช้จะออกจากหรือปิดหน้าเว็บ ลักษณะการทํางานนี้เหมาะสําหรับบีคอนสําหรับข้อมูลวิเคราะห์ แต่ไม่ค่อยเหมาะสําหรับการแก้ไขข้อบกพร่องแบบอินเทอร์แอกทีฟ
web-vitals
มีตัวเลือก reportAllChanges
สำหรับการรายงานแบบละเอียดมากขึ้น เมื่อเปิดใช้ ระบบจะไม่รายงานการโต้ตอบทุกครั้ง แต่ทุกครั้งที่มีการโต้ตอบช้ากว่าการโต้ตอบก่อนหน้า ระบบจะรายงานการโต้ตอบนั้น
ลองเพิ่มตัวเลือกลงในสคริปต์และโต้ตอบกับหน้าเว็บอีกครั้ง
<script type="module">
import {onINP} from './node_modules/web-vitals/dist/web-vitals.attribution.js';
onINP(console.log, {reportAllChanges: true});
</script>
รีเฟรชหน้าเว็บและตอนนี้ระบบควรรายงานการโต้ตอบไปยังคอนโซล โดยจะอัปเดตทุกครั้งที่มีรายการที่ช้าที่สุดรายการใหม่ เช่น ลองพิมพ์ในช่องค้นหา แล้วลบข้อมูลที่คุณป้อน
5. การระบุแหล่งที่มาประกอบด้วยข้อมูลใดบ้าง
มาเริ่มกันที่การโต้ตอบครั้งแรกที่สุดที่ผู้ใช้ส่วนใหญ่จะมีกับหน้าเว็บ ซึ่งก็คือกล่องโต้ตอบขอความยินยอมให้ใช้คุกกี้
หน้าเว็บจํานวนมากจะมีสคริปต์ที่ต้องทริกเกอร์คุกกี้แบบซิงค์กันเมื่อผู้ใช้ยอมรับคุกกี้ ทําให้การคลิกกลายเป็นการโต้ตอบที่ช้า นั่นคือสิ่งที่เกิดขึ้น
คลิกใช่เพื่อยอมรับคุกกี้ (เดโม) และดูข้อมูล INP ที่บันทึกไว้ในคอนโซลเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์
ข้อมูลระดับบนสุดนี้พร้อมใช้งานทั้งในรุ่นมาตรฐานและรุ่นการระบุแหล่งที่มาของ Web Vitals
{
name: 'INP',
value: 344,
rating: 'needs-improvement',
entries: [...],
id: 'v4-1715732159298-8028729544485',
navigationType: 'reload',
attribution: {...},
}
ระยะเวลานับจากตอนที่ผู้ใช้คลิกจนถึงการวาดภาพครั้งถัดไปคือ 344 มิลลิวินาที ซึ่งเป็น INP"ต้องปรับปรุง" อาร์เรย์ entries
มีค่า PerformanceEntry
ทั้งหมดที่เชื่อมโยงกับการโต้ตอบนี้ ซึ่งในกรณีนี้ก็คือเหตุการณ์การคลิกเพียงรายการเดียว
อย่างไรก็ตาม หากต้องการดูว่าเกิดอะไรขึ้นในช่วงเวลานี้ เราสนใจพร็อพเพอร์ตี้ attribution
มากที่สุด ในการสร้างข้อมูลการระบุแหล่งที่มา web-vitals
จะค้นหาเฟรมภาพเคลื่อนไหวแบบยาว (LoAF) ที่ซ้อนทับกับเหตุการณ์การคลิก จากนั้น LoAF จะแสดงข้อมูลโดยละเอียดเกี่ยวกับเวลาที่ใช้ไปในช่วงนั้น ตั้งแต่สคริปต์ที่ทำงานไปจนถึงเวลาที่ใช้ใน requestAnimationFrame
callback, สไตล์ และเลย์เอาต์
ขยายพร็อพเพอร์ตี้ attribution
เพื่อดูข้อมูลเพิ่มเติม ข้อมูลจะสมบูรณ์ยิ่งขึ้น
attribution: {
interactionTargetElement: Element,
interactionTarget: '#confirm',
interactionType: 'pointer',
inputDelay: 27,
processingDuration: 295.6,
presentationDelay: 21.4,
processedEventEntries: [...],
longAnimationFrameEntries: [...],
}
อันดับแรกคือข้อมูลเกี่ยวกับสิ่งที่มีการโต้ตอบ
interactionTargetElement
: การอ้างอิงแบบเรียลไทม์ไปยังองค์ประกอบที่มีการโต้ตอบ (หากยังไม่ได้นำองค์ประกอบออกจาก DOM)interactionTarget
: ตัวเลือกสำหรับค้นหาองค์ประกอบภายในหน้า
ถัดไปคือรายละเอียดของช่วงเวลาโดยสังเขป
inputDelay
: เวลาที่ผ่านไประหว่างที่ผู้ใช้เริ่มโต้ตอบ (เช่น คลิกเมาส์) กับเวลาที่ Listener เหตุการณ์สําหรับการโต้ตอบนั้นเริ่มทํางาน ในกรณีนี้ ความล่าช้าในการป้อนข้อมูลอยู่ที่ประมาณ 27 มิลลิวินาทีเท่านั้น แม้ว่าจะมีการจำกัด CPU ไว้ก็ตามprocessingDuration
: เวลาที่ Listener เหตุการณ์ใช้ในการทํางานจนเสร็จสมบูรณ์ บ่อยครั้งที่หน้าเว็บจะมี Listeners หลายรายการสําหรับเหตุการณ์เดียว (เช่นpointerdown
,pointerup
และclick
) หากทํางานในเฟรมภาพเคลื่อนไหวเดียวกันทั้งหมด ระบบจะรวมเหตุการณ์เหล่านี้ไว้ด้วยกันในกรอบเวลานี้ ในกรณีนี้ ระยะเวลาการประมวลผลใช้เวลา 295.6 มิลลิวินาที ซึ่งเป็นเวลาส่วนใหญ่ของ INPpresentationDelay
: ระยะเวลาตั้งแต่ที่ Listener เหตุการณ์ทํางานเสร็จสิ้นจนถึงเวลาที่เบราว์เซอร์วาดเฟรมถัดไปเสร็จ ในกรณีนี้คือ 21.4 มิลลิวินาที
ระยะ INP เหล่านี้อาจเป็นสัญญาณสําคัญสําหรับการวินิจฉัยสิ่งที่จําเป็นต้องเพิ่มประสิทธิภาพ คู่มือเพิ่มประสิทธิภาพ INP มีข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้
เมื่อเจาะลึกลงไปอีก processedEventEntries
มีเหตุการณ์ 5 รายการ ซึ่งต่างจากเหตุการณ์เดียวในอาร์เรย์ INP entries
ระดับบนสุด ความแตกต่างคืออะไร
processedEventEntries: [
{
name: 'mouseover',
entryType: 'event',
startTime: 1801.6,
duration: 344,
processingStart: 1825.3,
processingEnd: 1825.3,
cancelable: true
},
{
name: 'mousedown',
entryType: 'event',
startTime: 1801.6,
duration: 344,
processingStart: 1825.3,
processingEnd: 1825.3,
cancelable: true
},
{name: 'mousedown', ...},
{name: 'mouseup', ...},
{name: 'click', ...},
],
รายการระดับบนสุดคือเหตุการณ์ INP ซึ่งในกรณีนี้คือคลิก การระบุแหล่งที่มา processedEventEntries
คือเหตุการณ์ทั้งหมดที่ประมวลผลในเฟรมเดียวกัน โปรดทราบว่าเหตุการณ์นี้รวมเหตุการณ์อื่นๆ ด้วย เช่น mouseover
และ mousedown
ไม่ใช่แค่เหตุการณ์การคลิก การทราบเกี่ยวกับเหตุการณ์อื่นๆ เหล่านี้อาจมีความสำคัญหากเหตุการณ์เหล่านั้นช้าเช่นกัน เนื่องจากเหตุการณ์ทั้งหมดเหล่านี้ส่งผลให้การตอบสนองช้า
สุดท้ายคืออาร์เรย์ longAnimationFrameEntries
ข้อมูลนี้อาจเป็นรายการเดียว แต่ก็มีบางกรณีที่การโต้ตอบอาจกระจายไปทั่วเฟรมหลายเฟรม นี่เป็นกรณีที่ง่ายที่สุดที่มีเฟรมภาพเคลื่อนไหวยาวเฟรมเดียว
longAnimationFrameEntries
การขยายรายการ LoAF
longAnimationFrameEntries: [{
name: 'long-animation-frame',
startTime: 1823,
duration: 319,
renderStart: 2139.5,
styleAndLayoutStart: 2139.7,
firstUIEventTimestamp: 1801.6,
blockingDuration: 268,
scripts: [{...}]
}],
ค่าที่มีประโยชน์หลายค่าอยู่ในนี้ เช่น การแจกแจงเวลาที่ใช้จัดแต่ง บทความเกี่ยวกับ Long Animation Frames API จะอธิบายพร็อพเพอร์ตี้เหล่านี้อย่างละเอียด ตอนนี้เราสนใจพร็อพเพอร์ตี้ scripts
เป็นหลัก ซึ่งมีรายการที่ให้รายละเอียดเกี่ยวกับสคริปต์ที่ทำให้เกิดเฟรมที่ทำงานเป็นเวลานาน ดังนี้
scripts: [{
name: 'script',
invoker: 'BUTTON#confirm.onclick',
invokerType: 'event-listener',
startTime: 1828.6,
executionStart: 1828.6,
duration: 294,
sourceURL: 'http://localhost:8080/third-party/cmp.js',
sourceFunctionName: '',
sourceCharPosition: 1144
}]
ในกรณีนี้ เราบอกได้ว่าเวลาส่วนใหญ่ที่ใช้อยู่ใน event-listener
รายการเดียวที่เรียกใช้ในวันที่ BUTTON#confirm.onclick
เรายังดู URL แหล่งที่มาของสคริปต์และตำแหน่งอักขระของตำแหน่งที่กําหนดฟังก์ชันได้ด้วย
สรุป
ข้อมูลการระบุแหล่งที่มานี้ช่วยระบุอะไรเกี่ยวกับเคสนี้ได้บ้าง
- การโต้ตอบทริกเกอร์โดยการคลิกองค์ประกอบ
button#confirm
(จากattribution.interactionTarget
และพร็อพเพอร์ตี้invoker
ในรายการการระบุแหล่งที่มาของสคริปต์) - ใช้เวลาส่วนใหญ่ในการเรียกใช้ Listeners เหตุการณ์ (จาก
attribution.processingDuration
เทียบกับเมตริกทั้งหมดvalue
) - โค้ด Listener เหตุการณ์ที่ช้าเริ่มต้นจาก Listener การคลิกที่กําหนดไว้ใน
third-party/cmp.js
(จากscripts.sourceURL
)
ข้อมูลนี้เพียงพอที่จะทราบว่าต้องเพิ่มประสิทธิภาพที่ใด
6. Listener เหตุการณ์หลายรายการ
รีเฟรชหน้าเว็บเพื่อให้คอนโซลเครื่องมือสำหรับนักพัฒนาเว็บแสดงข้อมูลชัดเจนและเพื่อให้การโต้ตอบเกี่ยวกับความยินยอมในการใช้คุกกี้ไม่ใช่การโต้ตอบที่นานที่สุดอีกต่อไป
เริ่มพิมพ์ในช่องค้นหา ข้อมูลการระบุแหล่งที่มาแสดงอะไร คุณคิดว่าเกิดอะไรขึ้น
ข้อมูลการระบุแหล่งที่มา
ก่อนอื่น ให้สแกนระดับสูงของตัวอย่างการทดสอบเดโม 1 รายการ ดังนี้
{
name: 'INP',
value: 1072,
rating: 'poor',
attribution: {
interactionTargetElement: Element,
interactionTarget: '#search-terms',
interactionType: 'keyboard',
inputDelay: 3.3,
processingDuration: 1060.6,
presentationDelay: 8.1,
processedEventEntries: [...],
longAnimationFrameEntries: [...],
}
}
ค่า INP ไม่ดี (เปิดใช้การจำกัด CPU) จากการโต้ตอบด้วยแป้นพิมพ์กับองค์ประกอบ input#search-terms
ระยะเวลาส่วนใหญ่ (1,061 มิลลิวินาทีจาก INP ทั้งหมด 1,072 มิลลิวินาที) เป็นการประมวลผล
อย่างไรก็ตาม รายการ scripts
น่าสนใจกว่า
การย้ายข้อมูลเลย์เอาต์
รายการแรกของอาร์เรย์ scripts
ให้บริบทที่เป็นประโยชน์แก่เราดังนี้
scripts: [{
name: 'script',
invoker: 'BUTTON#confirm.onclick',
invokerType: 'event-listener',
startTime: 4875.6,
executionStart: 4875.6,
duration: 497,
forcedStyleAndLayoutDuration: 388,
sourceURL: 'http://localhost:8080/js/index.js',
sourceFunctionName: 'handleSearch',
sourceCharPosition: 940
},
...]
ระยะเวลาการประมวลผลส่วนใหญ่เกิดขึ้นระหว่างการเรียกใช้สคริปต์นี้ ซึ่งเป็น input
listener (ผู้เรียกใช้คือ INPUT#search-terms.oninput
) ระบบจะระบุชื่อฟังก์ชัน (handleSearch
) และตำแหน่งอักขระภายในไฟล์ต้นฉบับ index.js
อย่างไรก็ตาม มีพร็อพเพอร์ตี้ใหม่ forcedStyleAndLayoutDuration
นี่เป็นเวลาที่ใช้ในการเรียกใช้สคริปต์นี้ ซึ่งเบราว์เซอร์ต้องจัดเรียงหน้าเว็บใหม่ กล่าวคือ 78% ของเวลาที่ใช้ดำเนินการ Listener เหตุการณ์นี้ (388 มิลลิวินาทีจาก 497 มิลลิวินาที) เป็นการใช้เวลาในการเปลี่ยนเลย์เอาต์
ปัญหานี้ควรได้รับการแก้ไขเป็นลำดับต้นๆ
Listener ที่ซ้ำกัน
รายการสคริปต์ 2 รายการต่อไปนี้ไม่มีอะไรน่าทึ่งเป็นพิเศษ
scripts: [...,
{
name: 'script',
invoker: '#document.onkeyup',
invokerType: 'event-listener',
startTime: 5375.3,
executionStart: 5375.3,
duration: 124,
sourceURL: 'http://localhost:8080/js/index.js',
sourceFunctionName: '',
sourceCharPosition: 1526,
},
{
name: 'script',
invoker: '#document.onkeyup',
invokerType: 'event-listener',
startTime: 5673.9,
executionStart: 5673.9,
duration: 95,
sourceURL: 'http://localhost:8080/js/index.js',
sourceFunctionName: '',
sourceCharPosition: 1526
}]
ทั้ง 2 รายการเป็น keyup
listener ที่ทำงานทีละรายการ ตัวแฟังเป็นฟังก์ชันที่ไม่ระบุชื่อ (จึงไม่มีการรายงานในพร็อพเพอร์ตี้ sourceFunctionName
) แต่เรายังมีไฟล์ต้นทางและตําแหน่งอักขระ จึงค้นหาตําแหน่งโค้ดได้
สิ่งที่แปลกคือทั้ง 2 รายการมาจากไฟล์ต้นทางและตําแหน่งอักขระเดียวกัน
เบราว์เซอร์จึงประมวลผลการกดแป้นหลายครั้งในเฟรมภาพเคลื่อนไหวเฟรมเดียว ซึ่งทําให้โปรแกรมรับฟังเหตุการณ์นี้ทํางาน 2 ครั้งก่อนที่จะวาดอะไรได้
ผลลัพธ์นี้ยังอาจทวีความรุนแรงขึ้นด้วย เมื่อ Listener เหตุการณ์ใช้เวลานานขึ้นในการดำเนินการให้เสร็จสมบูรณ์ เหตุการณ์อินพุตเพิ่มเติมก็จะเข้ามามากขึ้น ซึ่งจะยืดเวลาการโต้ตอบที่ช้าให้นานขึ้นอีก
เนื่องจากเป็นการโต้ตอบแบบค้นหา/เติมข้อความอัตโนมัติ การกรองสัญญาณรบกวนจากอินพุตจึงเป็นกลยุทธ์ที่ดีเพื่อให้ระบบประมวลผลการกดแป้นพิมพ์ได้สูงสุด 1 ครั้งต่อเฟรม
7. ความล่าช้าของอินพุต
สาเหตุทั่วไปของความล่าช้าในการป้อนข้อมูลคือ เวลาที่ผ่านไปตั้งแต่ผู้ใช้โต้ตอบจนถึงเวลาที่โปรแกรมรับฟังเหตุการณ์เริ่มประมวลผลการโต้ตอบได้ เนื่องจากเธรดหลักไม่ว่าง ปัญหานี้อาจเกิดจากหลายสาเหตุ ดังนี้
- หน้าเว็บกำลังโหลดและเทรดหลักกำลังยุ่งอยู่กับงานเริ่มต้นของการตั้งค่า DOM, การจัดวางและการจัดสไตล์หน้าเว็บ ตลอดจนการประเมินและเรียกใช้สคริปต์
- โดยทั่วไปหน้าเว็บจะทำงานอยู่ เช่น กำลังประมวลผล แสดงภาพเคลื่อนไหวที่ใช้สคริปต์ หรือแสดงโฆษณา
- การโต้ตอบก่อนหน้านี้ใช้เวลาประมวลผลนานมากจนทำให้การโต้ตอบในอนาคตล่าช้า ซึ่งจะเห็นได้ในตัวอย่างสุดท้าย
หน้าเดโมมีฟีเจอร์ลับที่หากคุณคลิกโลโก้หอยทากที่ด้านบนของหน้า หน้าเว็บจะเริ่มแสดงภาพเคลื่อนไหวและทำงาน JavaScript ในเธรดหลักอย่างหนัก
- คลิกโลโก้หอยทากเพื่อเริ่มภาพเคลื่อนไหว
- งานที่เขียนด้วย JavaScript จะทริกเกอร์เมื่อหอยทากอยู่ที่ด้านล่างของการแสดงผล ลองโต้ตอบกับหน้าเว็บให้ใกล้เคียงกับจุดต่ำสุดของการตีกลับมากที่สุด และดูว่าคุณสามารถเรียกใช้ INP ได้มากน้อยเพียงใด
ตัวอย่างเช่น แม้ว่าคุณจะไม่ได้เรียกใช้ Listeners เหตุการณ์อื่นๆ เช่น จากการคลิกและโฟกัสช่องค้นหาทันทีที่ภาพหอยทากเด้งขึ้น การทำงานของเธรดหลักก็จะทำให้หน้าเว็บไม่ตอบสนองเป็นเวลานาน
ในหน้าเว็บจํานวนมาก การทำงานในเธรดหลักที่หนักหน่วงจะไม่ทำงานได้ดีเท่านี้ แต่นี่เป็นการสาธิตที่ดีในการดูว่าจะระบุการทำงานดังกล่าวในข้อมูลการระบุแหล่งที่มาของ INP ได้อย่างไร
ต่อไปนี้คือตัวอย่างการระบุแหล่งที่มาจากการโฟกัสเฉพาะช่องค้นหาระหว่างการตีกลับแบบหอยทาก
{
name: 'INP',
value: 728,
rating: 'poor',
attribution: {
interactionTargetElement: Element,
interactionTarget: '#search-terms',
interactionType: 'pointer',
inputDelay: 702.3,
processingDuration: 4.9,
presentationDelay: 20.8,
longAnimationFrameEntries: [{
name: 'long-animation-frame',
startTime: 2064.8,
duration: 790,
renderStart: 2065,
styleAndLayoutStart: 2854.2,
firstUIEventTimestamp: 0,
blockingDuration: 740,
scripts: [{...}]
}]
}
}
ตามที่คาดไว้ โปรแกรมฟังเหตุการณ์ทํางานอย่างรวดเร็ว โดยแสดงระยะเวลาการประมวลผล 4.9 มิลลิวินาที และการโต้ตอบที่ไม่ดีส่วนใหญ่เกิดขึ้นในช่วงที่การป้อนข้อมูลล่าช้า โดยใช้เวลา 702.3 มิลลิวินาทีจากทั้งหมด 728
สถานการณ์นี้อาจแก้ไขข้อบกพร่องได้ยาก แม้ว่าเราจะทราบว่าผู้ใช้โต้ตอบกับอะไรและอย่างไร แต่เราก็ทราบด้วยว่าการโต้ตอบส่วนนั้นเสร็จสมบูรณ์อย่างรวดเร็วและไม่ใช่ปัญหา แต่ปัญหาเกิดจากสิ่งอื่นในหน้าเว็บที่ทำให้การโต้ตอบล่าช้ากว่าที่ควรจะเป็น แต่เราจะรู้ได้อย่างไรว่าควรเริ่มมองหาที่ใด
รายการสคริปต์ LoAF จะช่วยแก้ปัญหานี้
scripts: [{
name: 'script',
invoker: 'SPAN.onanimationiteration',
invokerType: 'event-listener',
startTime: 2065,
executionStart: 2065,
duration: 788,
sourceURL: 'http://localhost:8080/js/index.js',
sourceFunctionName: 'cryptodaphneCoinHandler',
sourceCharPosition: 1831
}]
แม้ว่าฟังก์ชันนี้จะไม่มีส่วนเกี่ยวข้องกับการโต้ตอบ แต่ทําให้เฟรมภาพเคลื่อนไหวช้าลง จึงรวมอยู่ในข้อมูล LoAF ที่เข้าร่วมกับเหตุการณ์การโต้ตอบ
จากข้อมูลนี้ เราเห็นว่าฟังก์ชันที่ทำให้เกิดเวลาหน่วงในการประมวลผลอินเทอร์แอกชันทริกเกอร์อย่างไร (โดย animationiteration
listener) ฟังก์ชันใดที่รับผิดชอบ และอยู่ที่ไหนในไฟล์ต้นทาง
8. การเลื่อนเวลานำเสนอ: เมื่อการอัปเดตไม่แสดง
ความล่าช้าของการแสดงผลจะวัดเวลาตั้งแต่ที่โปรแกรมรับฟังเหตุการณ์ทํางานเสร็จสิ้นจนกว่าเบราว์เซอร์จะวาดเฟรมใหม่ไปยังหน้าจอได้ ซึ่งแสดงผลป้อนกลับที่ผู้ใช้มองเห็น
รีเฟรชหน้าเว็บเพื่อรีเซ็ตค่า INP อีกครั้ง แล้วเปิดเมนู 3 ขีด มีปัญหาแน่นอนเมื่อเปิด
หน้าตาของการดำเนินการนี้เป็นอย่างไร
{
name: 'INP',
value: 376,
rating: 'needs-improvement',
delta: 352,
attribution: {
interactionTarget: '#sidenav-button>svg',
interactionType: 'pointer',
inputDelay: 12.8,
processingDuration: 14.7,
presentationDelay: 348.5,
longAnimationFrameEntries: [{
name: 'long-animation-frame',
startTime: 651,
duration: 365,
renderStart: 673.2,
styleAndLayoutStart: 1004.3,
firstUIEventTimestamp: 138.6,
blockingDuration: 315,
scripts: [{...}]
}]
}
}
ครั้งนี้เป็นการเลื่อนเวลาแสดงที่ทําให้การโต้ตอบช้าส่วนใหญ่ ซึ่งหมายความว่าสิ่งที่บล็อกเธรดหลักจะเกิดขึ้นหลังจากที่โปรแกรมรับฟังเหตุการณ์ทำงานเสร็จแล้ว
scripts: [{
entryType: 'script',
invoker: 'FrameRequestCallback',
invokerType: 'user-callback',
startTime: 673.8,
executionStart: 673.8,
duration: 330,
sourceURL: 'http://localhost:8080/js/side-nav.js',
sourceFunctionName: '',
sourceCharPosition: 1193,
}]
เมื่อดูรายการเดียวในอาร์เรย์ scripts
เราจะเห็นว่ามีการใช้เวลาใน user-callback
จาก FrameRequestCallback
ครั้งนี้ความล่าช้าในการนำเสนอเกิดจากคอลแบ็ก requestAnimationFrame
9. บทสรุป
การรวบรวมข้อมูลภาคสนาม
โปรดทราบว่าการดำเนินการทั้งหมดนี้ง่ายขึ้นเมื่อดูรายการการระบุแหล่งที่มา INP รายการเดียวจากการโหลดหน้าเว็บครั้งเดียว ข้อมูลนี้รวบรวมเพื่อแก้ไขข้อบกพร่อง INP ตามข้อมูลภาคสนามได้อย่างไร รายละเอียดที่เป็นประโยชน์จำนวนมากกลับทำให้การดำเนินการนี้ยากขึ้น
ตัวอย่างเช่น การทราบว่าองค์ประกอบหน้าเว็บใดเป็นแหล่งที่มาของการโต้ตอบที่ช้านั้นมีประโยชน์อย่างยิ่ง อย่างไรก็ตาม หากหน้าเว็บมีชื่อคลาส CSS ที่คอมไพล์แล้วซึ่งเปลี่ยนแปลงไปในแต่ละบิลด์ ตัวเลือก web-vitals
จากองค์ประกอบเดียวกันอาจแตกต่างกันไปในแต่ละบิลด์
แต่คุณต้องพิจารณาแอปพลิเคชันเฉพาะของคุณเพื่อพิจารณาว่าข้อมูลใดมีประโยชน์มากที่สุดและจะรวบรวมข้อมูลได้อย่างไร ตัวอย่างเช่น ก่อนที่จะส่งข้อมูลการระบุแหล่งที่มาของบีคอนกลับ คุณอาจแทนที่ตัวเลือก web-vitals
ด้วยตัวระบุของคุณเอง โดยอิงตามคอมโพเนนต์ที่มีเป้าหมายอยู่ หรือบทบาท ARIA ที่เป้าหมายมี
ในทํานองเดียวกัน รายการ scripts
อาจมีแฮชที่อิงตามไฟล์ในเส้นทาง sourceURL
ซึ่งทําให้รวมกันยาก แต่คุณสามารถตัดแฮชตามกระบวนการสร้างที่คุณทราบก่อนที่จะส่งสัญญาณข้อมูลกลับได้
ขออภัย เราไม่พบเส้นทางที่ง่ายดายสำหรับข้อมูลที่ซับซ้อนขนาดนี้ แต่การใช้ข้อมูลชุดย่อยก็มีประโยชน์มากกว่าการไม่มีข้อมูลการระบุแหล่งที่มาเลยสำหรับกระบวนการแก้ไขข้อบกพร่อง
การระบุแหล่งที่มาในทุกที่
การระบุแหล่งที่มาของ INP อิงตาม LoAF เป็นตัวช่วยแก้ไขข้อบกพร่องที่มีประสิทธิภาพ ซึ่งจะให้ข้อมูลโดยละเอียดเกี่ยวกับสิ่งที่เกิดขึ้นในระหว่าง INP ในหลายกรณี รายงานนี้สามารถชี้ไปยังตําแหน่งในสคริปต์ที่แน่นอนซึ่งคุณควรเริ่มเพิ่มประสิทธิภาพ
ตอนนี้คุณพร้อมที่จะใช้ข้อมูลการระบุแหล่งที่มาของ INP ในเว็บไซต์ใดก็ได้
แม้ว่าคุณจะไม่มีสิทธิ์แก้ไขหน้าเว็บ แต่ก็สามารถสร้างกระบวนการนี้ขึ้นมาใหม่จากโค้ดแล็บนี้ได้โดยเรียกใช้สnippet ต่อไปนี้ในคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บเพื่อดูสิ่งที่พบ
const script = document.createElement('script');
script.src = 'https://unpkg.com/web-vitals@4/dist/web-vitals.attribution.iife.js';
script.onload = function () {
webVitals.onINP(console.log, {reportAllChanges: true});
};
document.head.appendChild(script);