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

1. บทนำ

นี่คือ Codelab แบบอินเทอร์แอกทีฟสําหรับการเรียนรู้วิธีวัด Interaction to 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/ ในเบราว์เซอร์

ลองใช้หน้าเว็บ

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

ภาพหน้าจอของหน้าเดโม Gastropodicon

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

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

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

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

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

ภาพหน้าจอของแผงประสิทธิภาพใน DevTools ข้างแอป โดยเลือกการทํางานของ 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>

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

ภาพหน้าจอของคอนโซล DevTools ที่มีข้อความ INP ที่พิมพ์ออกมาเรียบร้อยแล้ว

5. การระบุแหล่งที่มาประกอบด้วยข้อมูลใดบ้าง

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

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

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

ออบเจ็กต์ข้อมูล 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 มิลลิวินาที ซึ่งเป็นเวลาส่วนใหญ่ของ INP
  • presentationDelay: ระยะเวลาตั้งแต่ที่ 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);

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