การวัดการโต้ตอบกับ Next Paint (INP)

1. บทนำ

นี่คือ Codelab แบบอินเทอร์แอกทีฟสำหรับดูวิธีวัดการโต้ตอบกับ Next Paint (INP) โดยใช้ไลบรารี web-vitals

ข้อกำหนดเบื้องต้น

สิ่งที่คุณจะได้เรียนรู้

  • วิธีเพิ่มไลบรารี web-vitals ลงในหน้าและใช้ข้อมูลการระบุแหล่งที่มา
  • ใช้ข้อมูลการระบุแหล่งที่มาเพื่อวิเคราะห์จุดและวิธีเริ่มปรับปรุง INP

สิ่งที่ต้องมี

  • คอมพิวเตอร์ที่สามารถโคลนโค้ดจาก GitHub และเรียกใช้คำสั่ง npm
  • เครื่องมือแก้ไขข้อความ
  • Chrome เวอร์ชันล่าสุดที่ช่วยให้การวัดการโต้ตอบทั้งหมดทำงานได้

2. ตั้งค่า

รับและเรียกใช้โค้ด

โค้ดอยู่ในที่เก็บของ web-vitals-codelabs

  1. โคลนที่เก็บในเทอร์มินัลของคุณ: git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git
  2. สำรวจไดเรกทอรีที่โคลน: cd web-vitals-codelabs/measuring-inp
  3. ติดตั้งการอ้างอิง: npm ci
  4. เริ่มต้นเว็บเซิร์ฟเวอร์: npm run start
  5. ไปที่ http://localhost:8080/ ในเบราว์เซอร์

ลองใช้หน้านี้

Codelab นี้ใช้ Gastropodicon (เว็บไซต์อ้างอิงกายวิภาคของหอยทากที่ได้รับความนิยม) เพื่อสำรวจปัญหาที่อาจเกิดขึ้นกับ INP

ภาพหน้าจอของหน้าสาธิต Gastropodicon

ลองโต้ตอบกับหน้าเว็บเพื่อดูว่าการโต้ตอบใดช้า

3. เริ่มตั้งค่าเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome

เปิดเครื่องมือสำหรับนักพัฒนาเว็บจากเครื่องมือเพิ่มเติม > เมนูเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์โดยคลิกขวาที่หน้าเว็บ แล้วเลือกตรวจสอบ หรือโดยใช้แป้นพิมพ์ลัด

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

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

ภาพหน้าจอของแผงประสิทธิภาพของเครื่องมือสำหรับนักพัฒนาเว็บที่ด้านข้างแอป ซึ่งเลือกทำให้ CPU ช้าลง 4 เท่า

4. กำลังติดตั้ง วิตามินเว็บ

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>

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

ภาพหน้าจอของคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บที่มีข้อความ INP ที่พิมพ์ข้อความลงในคอนโซลสำเร็จแล้ว

5. มีอะไรอยู่ในการระบุแหล่งที่มา

มาเริ่มกันที่การโต้ตอบแรกที่ผู้ใช้ส่วนใหญ่จะมีกับหน้าเว็บ นั่นคือกล่องโต้ตอบความยินยอมในการใช้คุกกี้

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

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

บันทึกออบเจ็กต์ข้อมูล INP ไปยังคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บแล้ว

ข้อมูลระดับบนสุดนี้มีอยู่ในทั้งการสร้างส่วนขยายเว็บแบบมาตรฐานและการระบุแหล่งที่มา:

{
  name: 'INP',
  value: 344,
  rating: 'needs-improvement',
  entries: [...],
  id: 'v4-1715732159298-8028729544485',
  navigationType: 'reload',
  attribution: {...},
}

ระยะเวลานับตั้งแต่ที่ผู้ใช้คลิกไปยังการแสดงผลถัดไปคือ 344 มิลลิวินาที "ต้องปรับปรุง" INP อาร์เรย์ entries มีค่า PerformanceEntry ทั้งหมดที่เชื่อมโยงกับการโต้ตอบนี้ ในกรณีนี้คือเหตุการณ์การคลิกเพียงครั้งเดียว

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

ขยายพร็อพเพอร์ตี้ 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) หากทุกภาพทำงานในเฟรมภาพเคลื่อนไหวเดียวกัน ระบบจะรวมเข้าไว้ด้วยกันในช่วงเวลานี้ ในกรณีนี้ ระยะเวลาการประมวลผลจะใช้เวลา 295.6 มิลลิวินาที ซึ่งเป็นเวลาจำนวนมากของเวลา INP
  • presentationDelay: ระยะเวลาตั้งแต่ที่ Listener เหตุการณ์ทำงานเสร็จจนถึงเวลาที่เบราว์เซอร์วาดเฟรมถัดไปเสร็จ ในกรณีนี้คือ 21.4 มิลลิวินาที

เฟส INP เหล่านี้อาจเป็นสัญญาณสำคัญในการวินิจฉัยว่าต้องเพิ่มประสิทธิภาพสิ่งใด คำแนะนำเกี่ยวกับ INP ของ Optimize มีข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้

หากวิเคราะห์ให้ลึกลงไปอีกเล็กน้อย พบว่า processedEventEntries มีเหตุการณ์ 5 รายการ ซึ่งตรงข้ามกับเหตุการณ์เดียวในอาร์เรย์ entries INP ระดับบนสุด แล้วมีความแตกต่างกันอย่างไร

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 ในรายการการระบุแหล่งที่มาของสคริปต์)
  • เวลาที่ใช้ดำเนินการ Listener เหตุการณ์เป็นหลัก (จาก 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
},
...]

ระยะเวลาการประมวลผลส่วนใหญ่เกิดขึ้นระหว่างการดำเนินการสคริปต์นี้ ซึ่งเป็น Listener input (ผู้เรียกใช้คือ 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: 5673.9,
  duration: 95,

  sourceURL: 'http://localhost:8080/js/index.js',
  sourceFunctionName: '',
  sourceCharPosition: 1526
}]

ทั้ง 2 รายการคือ Listener keyup รายการ โดยเรียกใช้ทีละรายการ Listener เป็นฟังก์ชันที่ไม่ระบุชื่อ (จึงไม่มีการรายงานในพร็อพเพอร์ตี้ sourceFunctionName) แต่เรายังคงมีไฟล์ต้นฉบับและตำแหน่งอักขระอยู่ เพื่อให้เราหาตำแหน่งของโค้ดได้

ที่แปลกคือทั้งคู่มาจากไฟล์ต้นฉบับและตำแหน่งอักขระเดียวกัน

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

เอฟเฟกต์นี้ก็รวมได้เช่นกัน โดยยิ่ง Listener เหตุการณ์ใช้เวลานานเท่าไหร่ ยิ่งมีเหตุการณ์อินพุตเข้ามามากขึ้นเท่านั้น ซึ่งทำให้การโต้ตอบช้าลงได้นานขึ้นมาก

เนื่องจากนี่เป็นการโต้ตอบการค้นหา/การเติมข้อความอัตโนมัติ การดีดกลับอินพุตจึงเป็นกลยุทธ์ที่ดี โดยมีการประมวลผลการกดแป้นไม่เกิน 1 รายการต่อเฟรม

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

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

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

หน้าเดโมมีฟีเจอร์ลับซึ่งหากคุณคลิกโลโก้หอยทากที่ด้านบนของหน้า โลโก้ดังกล่าวจะเริ่มเคลื่อนไหวและการทำงานของ JavaScript เทรดหลักที่มีจำนวนมาก

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

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

ในหลายๆ หน้า เทรดหลักที่มีจำนวนมากจะทำงานได้ไม่ดีนัก แต่นี่เป็นการสาธิตที่ดีสำหรับการดูว่าจะสามารถระบุตัวตนในข้อมูลการระบุแหล่งที่มา 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: [{...}]
    }]
  }
}

ตามที่คาดการณ์ไว้ Listener เหตุการณ์ดำเนินการอย่างรวดเร็ว โดยแสดงระยะเวลาการประมวลผลอยู่ที่ 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 ที่รวมกับเหตุการณ์การโต้ตอบ

จากส่วนนี้ เราจะดูได้ว่าฟังก์ชันที่ล่าช้าในการประมวลผลการโต้ตอบถูกเรียกขึ้นมาอย่างไร (โดย Listener animationiteration) ฟังก์ชันใดเป็นต้นเหตุของฟังก์ชัน และตำแหน่งของฟังก์ชันในไฟล์ต้นฉบับของเรา

8. ความล่าช้าของงานนำเสนอ: เมื่อการอัปเดตไม่แสดงผล

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

รีเฟรชหน้านี้เพื่อรีเซ็ตค่า 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: [{...}]
    }]
  }
}

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

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 รายการเดียวจากการโหลดหน้าเว็บ 1 ครั้ง ระบบจะรวบรวมข้อมูลนี้เพื่อแก้ไขข้อบกพร่อง INP ตามข้อมูลภาคสนามได้อย่างไร รายละเอียดที่เป็นประโยชน์ทำให้เรื่องนี้ทำได้ยากขึ้น

ตัวอย่างเช่น การทราบว่าองค์ประกอบของหน้าใดเป็นแหล่งที่มาทั่วไปของการโต้ตอบที่ช้านั้นจะเป็นประโยชน์มาก อย่างไรก็ตาม หากหน้าได้รวบรวมชื่อคลาส CSS ที่เปลี่ยนจากบิลด์หนึ่งเป็นบิลด์ ตัวเลือก web-vitals จากองค์ประกอบเดียวกันอาจไม่เหมือนกันในแต่ละบิลด์

แต่คุณจะต้องนึกถึงแอปพลิเคชันของคุณ โดยพิจารณาจากข้อมูลที่มีประโยชน์ที่สุดและวิธีการรวบรวมข้อมูล เช่น ก่อนที่จะบีคอนข้อมูลการระบุแหล่งที่มากลับมา คุณอาจแทนที่ตัวเลือก web-vitals ด้วยตัวระบุของคุณเองได้ โดยอิงตามคอมโพเนนต์ที่เป้าหมายนั้นอยู่ หรือบทบาท ARIA ที่เป้าหมายดำเนินการ

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

ขออภัย ไม่มีเส้นทางง่ายๆ ที่มีข้อมูลที่ซับซ้อนเช่นนี้ แต่การใช้ส่วนย่อยๆ ก็มีคุณค่ามากกว่าการไม่มีข้อมูลการระบุแหล่งที่มาเลยสำหรับขั้นตอนการแก้ไขข้อบกพร่อง

ระบุแหล่งที่มาได้ทุกที่

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

ตอนนี้คุณก็พร้อมใช้ข้อมูลการระบุแหล่งที่มา INP ในเว็บไซต์แล้ว

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

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);

ดูข้อมูลเพิ่มเติม