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 - ติดตั้งทรัพยากร Dependency:
npm ci - เริ่มเว็บเซิร์ฟเวอร์:
npm run start - ไปที่ http://localhost:8080/ ในเบราว์เซอร์
ลองใช้หน้าเว็บ
Codelab นี้ใช้ Gastropodicon (เว็บไซต์อ้างอิงกายวิภาคของหอยทากยอดนิยม) เพื่อสำรวจปัญหาที่อาจเกิดขึ้นกับ INP

ลองโต้ตอบกับหน้าเว็บเพื่อดูว่าการโต้ตอบใดช้า
3. การเริ่มต้นใช้งานเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome
เปิดเครื่องมือสำหรับนักพัฒนาเว็บจากเมนูเครื่องมือเพิ่มเติม > เครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ โดยคลิกขวาที่หน้าเว็บแล้วเลือกตรวจสอบ หรือใช้แป้นพิมพ์ลัด
ในโค้ดแล็บนี้ เราจะใช้ทั้งแผงประสิทธิภาพและคอนโซล คุณสลับระหว่างเครื่องมือเหล่านี้ได้ทุกเมื่อในแท็บที่ด้านบนของ DevTools
- ปัญหา INP มักเกิดขึ้นในอุปกรณ์เคลื่อนที่ ดังนั้นให้เปลี่ยนไปใช้การจำลองการแสดงผลบนอุปกรณ์เคลื่อนที่
- หากคุณทดสอบบนเดสก์ท็อปหรือแล็ปท็อป ประสิทธิภาพอาจดีกว่าบนอุปกรณ์เคลื่อนที่จริงอย่างมาก หากต้องการดูประสิทธิภาพที่สมจริงยิ่งขึ้น ให้คลิกเฟืองที่ด้านขวาบนของแผงประสิทธิภาพ แล้วเลือกCPU ช้าลง 4 เท่า

4. การติดตั้ง web-vitals
web-vitals คือไลบรารี JavaScript สำหรับวัดเมตริก Web Vitals ที่ผู้ใช้ได้รับ คุณสามารถใช้ไลบรารีเพื่อบันทึกค่าเหล่านั้น แล้วส่งบีคอนไปยังปลายทางของข้อมูลวิเคราะห์เพื่อการวิเคราะห์ในภายหลัง โดยมีวัตถุประสงค์เพื่อพิจารณาว่าการโต้ตอบที่ช้าเกิดขึ้นเมื่อใดและที่ใด
คุณเพิ่มไลบรารีลงในหน้าเว็บได้หลายวิธี วิธีติดตั้งไลบรารีในเว็บไซต์ของคุณเองจะขึ้นอยู่กับวิธีจัดการทรัพยากร Dependency กระบวนการบิลด์ และปัจจัยอื่นๆ โปรดดูเอกสารของไลบรารีเพื่อดูตัวเลือกทั้งหมด
Codelab นี้จะติดตั้งจาก npm และโหลดสคริปต์โดยตรงเพื่อหลีกเลี่ยงการเจาะลึกกระบวนการบิลด์ที่เฉพาะเจาะจง
web-vitals มี 2 เวอร์ชันที่คุณใช้ได้ ดังนี้
- ควรใช้บิลด์ "มาตรฐาน" หากต้องการติดตามค่าเมตริกของ Core Web Vitals เมื่อโหลดหน้าเว็บ
- บิลด์ "การระบุแหล่งที่มา" จะเพิ่มข้อมูลการแก้ไขข้อบกพร่องเพิ่มเติมลงในเมตริกแต่ละรายการเพื่อวินิจฉัยว่าเหตุใดเมตริกจึงมีค่าตามที่แสดง
เราต้องการให้มีการสร้างการระบุแหล่งที่มาเพื่อวัด INP ใน Codelab นี้
เพิ่ม 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 ที่บันทึกไว้ในคอนโซล DevTools

ข้อมูลระดับบนสุดนี้พร้อมใช้งานทั้งใน Web Vitals มาตรฐานและ Web Vitals การระบุแหล่งที่มา
{
name: 'INP',
value: 344,
rating: 'needs-improvement',
entries: [...],
id: 'v4-1715732159298-8028729544485',
navigationType: 'reload',
attribution: {...},
}
ระยะเวลาตั้งแต่ที่ผู้ใช้คลิกจนถึงการแสดงผลครั้งถัดไปคือ 344 มิลลิวินาที ซึ่งเป็น INP "ต้องปรับปรุง" entries อาร์เรย์มีค่า PerformanceEntry ทั้งหมดที่เชื่อมโยงกับการโต้ตอบนี้ ในกรณีนี้คือเหตุการณ์คลิกเพียงรายการเดียว
แต่เราสนใจพร็อพเพอร์ตี้ attribution มากที่สุดเพื่อดูว่าเกิดอะไรขึ้นในช่วงเวลานี้ ในการสร้างข้อมูลการระบุแหล่งที่มา web-vitals จะค้นหา Long Animations Frame (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 เหตุการณ์ใช้ในการทำงานจนเสร็จสมบูรณ์ โดยทั่วไป หน้าเว็บจะมี Listener หลายรายการสําหรับเหตุการณ์เดียว (เช่นpointerdown,pointerupและclick) หาก Listener ทั้งหมดทํางานในเฟรมภาพเคลื่อนไหวเดียวกัน ระบบจะรวม Listener เหล่านี้ไว้ในเวลานี้ ในกรณีนี้ ระยะเวลาการประมวลผลจะใช้เวลา 295.6 มิลลิวินาที ซึ่งเป็นระยะเวลา INP ส่วนใหญ่presentationDelay: เวลาตั้งแต่ Listener เหตุการณ์ทํางานเสร็จจนถึงเวลาที่เบราว์เซอร์แสดงผลเฟรมถัดไปเสร็จ ในกรณีนี้คือ 21.4 มิลลิวินาที
ระยะ INP เหล่านี้อาจเป็นสัญญาณสำคัญในการวินิจฉัยสิ่งที่ต้องเพิ่มประสิทธิภาพ คู่มือการเพิ่มประสิทธิภาพ INP มีข้อมูลเพิ่มเติมในหัวข้อนี้
เมื่อเจาะลึกลงไปอีกเล็กน้อย processedEventEntries จะมีเหตุการณ์ห้ารายการ ซึ่งต่างจากเหตุการณ์เดียวในอาร์เรย์ 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ในรายการการระบุแหล่งที่มาของสคริปต์) - เวลาส่วนใหญ่ใช้ในการเรียกใช้เครื่องมือฟังเหตุการณ์ (จาก
attribution.processingDurationเทียบกับเมตริกทั้งหมดvalue) - โค้ด Listener เหตุการณ์ที่ทำงานช้าจะเริ่มต้นจาก Listener การคลิกที่กำหนดไว้ใน
third-party/cmp.js(จากscripts.sourceURL)
ซึ่งเป็นข้อมูลที่เพียงพอที่จะทราบว่าเราต้องเพิ่มประสิทธิภาพที่ใด
6. Listener เหตุการณ์หลายรายการ
รีเฟรชหน้าเว็บเพื่อให้คอนโซลเครื่องมือสำหรับนักพัฒนาเว็บสะอาด และการโต้ตอบความยินยอมในการใช้คุกกี้ไม่ใช่การโต้ตอบที่ยาวนานที่สุดอีกต่อไป
เริ่มพิมพ์ในช่องค้นหา ข้อมูลการระบุแหล่งที่มาแสดงอะไร คุณคิดว่าเกิดอะไรขึ้น
ข้อมูลการระบุแหล่งที่มา
ก่อนอื่น เรามาดูภาพรวมของตัวอย่างการทดสอบเดโมกัน
{
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% ของเวลาทั้งหมด (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: