เมตริกที่กำหนดเอง

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

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

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

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

  • เวลาที่แอปหน้าเว็บเดียว (SPA) เปลี่ยนจาก "หน้า" หนึ่งไปยังอีกหน้าหนึ่ง
  • ระยะเวลาที่หน้าเว็บใช้ในการแสดงข้อมูลที่ดึงจากฐานข้อมูลสำหรับผู้ใช้ที่เข้าสู่ระบบ
  • ระยะเวลาที่แอปที่แสดงผลฝั่งเซิร์ฟเวอร์ (SSR) ใช้ในการเพิ่มปริมาณน้ำ
  • อัตราการค้นพบแคชสำหรับทรัพยากรที่โหลดโดยผู้เข้าชมที่กลับมา
  • เวลาในการตอบสนองของเหตุการณ์การคลิกหรือเหตุการณ์บนแป้นพิมพ์ในเกม

API สำหรับวัดเมตริกที่กำหนดเอง

ที่ผ่านมานักพัฒนาเว็บไม่เคยมี API ระดับต่ำไว้วัดประสิทธิภาพมากนัก ด้วยเหตุนี้ จึงต้องพึ่งพาการแฮ็กเพื่อวัดว่าเว็บไซต์มีประสิทธิภาพดีหรือไม่

ตัวอย่างเช่น คุณระบุได้ว่าเทรดหลักถูกบล็อกเนื่องจากงาน JavaScript ที่ใช้เวลาดำเนินการนานหรือไม่โดยการเรียกใช้ลูป requestAnimationFrame และคำนวณเดลต้าระหว่างแต่ละเฟรม หากเดลต้านานกว่าอัตราเฟรมของจอแสดงผลมาก คุณสามารถรายงานเป็นงานที่ใช้เวลานาน แต่เราไม่แนะนำให้ใช้การแฮ็กดังกล่าวเนื่องจากอาจส่งผลกับประสิทธิภาพการทำงานโดยตรง (เช่น จะทำให้แบตเตอรี่หมดเร็ว)

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

ผู้สังเกตการณ์ประสิทธิภาพ

การสนับสนุนเบราว์เซอร์

  • 52
  • 79
  • 57
  • 11

แหล่งที่มา

การทำความเข้าใจ PerformanceObserver API สำคัญต่อการสร้างเมตริกประสิทธิภาพที่กำหนดเองเนื่องจากเป็นกลไกในการรับข้อมูลจาก API ประสิทธิภาพอื่นๆ ทั้งหมดที่กล่าวถึงในบทความนี้

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

คุณสร้าง PerformanceObserver โดยการส่งโค้ดเรียกกลับให้เรียกใช้ทุกครั้งที่มีการส่งรายการประสิทธิภาพใหม่ จากนั้น คุณบอกให้ผู้สังเกตการณ์ทราบถึงประเภทของรายการที่จะฟังผ่านเมธอด observe() ดังนี้

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });

  po.observe({type: 'some-entry-type'});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

ส่วนด้านล่างแสดงรายการประเภทต่างๆ ทั้งหมดที่ใช้ได้สำหรับสังเกต แต่ในเบราว์เซอร์ใหม่ๆ คุณสามารถตรวจสอบประเภทรายการที่ใช้ได้ผ่านพร็อพเพอร์ตี้ PerformanceObserver.supportedEntryTypes แบบคงที่

การสังเกตการณ์รายการที่เกิดขึ้นแล้ว

โดยค่าเริ่มต้น ออบเจ็กต์ PerformanceObserver รายการจะสังเกตรายการได้เมื่อเกิดขึ้นเท่านั้น ปัญหานี้อาจเกิดขึ้นหากคุณต้องการโหลดโค้ดวิเคราะห์ประสิทธิภาพแบบ Lazy Loading (เพื่อไม่ให้บล็อกทรัพยากรที่มีลำดับความสำคัญสูงกว่า)

หากต้องการดูรายการในอดีต (หลังจากเกิดขึ้น) ให้ตั้งค่าแฟล็ก buffered เป็น true เมื่อคุณเรียกใช้ observe() เบราว์เซอร์จะรวมรายการประวัติจากบัฟเฟอร์รายการประสิทธิภาพเมื่อมีการเรียกโค้ดเรียกกลับ PerformanceObserver ของคุณเป็นครั้งแรก

po.observe({
  type: 'some-entry-type',
  buffered: true,
});

API ประสิทธิภาพเดิมที่ควรหลีกเลี่ยง

ก่อนที่จะใช้งาน Performance Observer API นักพัฒนาซอฟต์แวร์จะเข้าถึงรายการประสิทธิภาพได้โดยใช้เมธอด 3 รายการต่อไปนี้ที่กำหนดไว้ในออบเจ็กต์ performance

แม้ว่า ระบบจะยังรองรับ API เหล่านี้อยู่ เราไม่แนะนำให้ใช้งาน เนื่องจาก API เหล่านี้ไม่อนุญาตให้คุณคอยฟังเมื่อมีการปล่อยรายการใหม่ นอกจากนี้ API ใหม่จำนวนมาก (เช่น งานที่ใช้เวลานาน) จะไม่แสดงผ่านออบเจ็กต์ performance แต่จะแสดงผ่าน PerformanceObserver เท่านั้น

ขอแนะนําให้หลีกเลี่ยงการใช้วิธีการเหล่านี้ในโค้ดและใช้ PerformanceObserver นับจากนี้เป็นต้นไป ยกเว้นกรณีที่คุณต้องการความเข้ากันได้ของ Internet Explorer โดยเฉพาะ

API ระยะเวลาของผู้ใช้

User Timing API คือ API การวัดผลวัตถุประสงค์ทั่วไปสำหรับเมตริกที่อิงตามเวลา ซึ่งจะช่วยให้คุณทำเครื่องหมายคะแนนในเวลาที่ต้องการได้ แล้ววัดระยะเวลาระหว่างเครื่องหมายเหล่านั้นในภายหลัง

// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();
// Record the time immediately after running a task.
performance.mark('myTask:end');

// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');

แม้ว่า API อย่าง Date.now() หรือ performance.now() จะให้ความสามารถที่คล้ายกันแก่คุณ แต่ข้อดีของการใช้ User Timing API คือผสานการทำงานกับเครื่องมือเพิ่มประสิทธิภาพได้เป็นอย่างดี ตัวอย่างเช่น เครื่องมือสำหรับนักพัฒนาเว็บใน Chrome แสดงภาพการวัดระยะเวลาของผู้ใช้ในแผงประสิทธิภาพ และผู้ให้บริการวิเคราะห์หลายรายจะติดตามการวัดผลทั้งหมดที่คุณทำและส่งข้อมูลระยะเวลาไปยังแบ็กเอนด์การวิเคราะห์โดยอัตโนมัติด้วย

หากต้องการรายงานการวัดระยะเวลาของผู้ใช้ คุณสามารถใช้ PerformanceObserver และลงทะเบียนเพื่อดูรายการประเภท measure ดังนี้

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });
  // Start listening for `measure` entries to be dispatched.
  po.observe({type: 'measure', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

API ของ Tasks แบบยาว

การสนับสนุนเบราว์เซอร์

  • 58
  • 79
  • x
  • x

แหล่งที่มา

Long Tasks API มีประโยชน์ในกรณีที่คุณทราบเมื่อมีการบล็อกเทรดหลักของเบราว์เซอร์เป็นเวลานานพอที่จะส่งผลต่ออัตราเฟรมหรือเวลาในการตอบสนองของอินพุต ปัจจุบัน API จะรายงานทุกงานที่ดำเนินการนานกว่า 50 มิลลิวินาที (ms)

เมื่อใดก็ตามที่คุณจำเป็นต้องเรียกใช้โค้ดที่มีราคาแพง (หรือโหลดและเรียกใช้สคริปต์ขนาดใหญ่) คุณควรติดตามว่าโค้ดดังกล่าวบล็อกเทรดหลักหรือไม่ อันที่จริงแล้ว เมตริกระดับสูงจำนวนมากสร้างขึ้นจาก Long Tasks API เอง (เช่น Time to Interactive (TTI) และ Total block Time (TBT))

หากต้องการทราบว่ามีงานที่ใช้เวลานานเมื่อใด คุณสามารถใช้ PerformanceObserver และลงทะเบียนเพื่อดูรายการประเภท longtask ดังนี้

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });
  // Start listening for `longtask` entries to be dispatched.
  po.observe({type: 'longtask', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

Element Timing API

การสนับสนุนเบราว์เซอร์

  • 77
  • 79
  • x
  • x

แหล่งที่มา

เมตริก Largest Contentful Paint (LCP) มีประโยชน์สำหรับการรู้เมื่อมีการระบายสีรูปภาพหรือบล็อกข้อความที่ใหญ่ที่สุดลงบนหน้าจอ แต่ในบางกรณีคุณต้องการวัดเวลาในการแสดงผลขององค์ประกอบอื่น

ในกรณีเหล่านี้ คุณใช้ Element Timing API ได้ อันที่จริง Largest ContentfulPaint API สร้างขึ้นจาก Element Timing API และเพิ่มการรายงานอัตโนมัติเกี่ยวกับองค์ประกอบที่มีเนื้อหาขนาดใหญ่ที่สุดได้ด้วยเช่นกัน แต่คุณสามารถรายงานเกี่ยวกับองค์ประกอบเพิ่มเติมได้โดยเพิ่มแอตทริบิวต์ elementtiming เข้าไปอย่างชัดเจน และลงทะเบียน PerformanceObserver เพื่อสังเกตประเภทรายการองค์ประกอบ

<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
...
<script>
// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((entryList) => {
    for (const entry of entryList.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });
  // Start listening for `element` entries to be dispatched.
  po.observe({type: 'element', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}
</script>

API การจับเวลาเหตุการณ์

เมตริก First Input Delay (FID) จะวัดระยะเวลาตั้งแต่ที่ผู้ใช้โต้ตอบกับหน้าเว็บเป็นครั้งแรกจนถึงตอนที่เบราว์เซอร์เริ่มประมวลผลเครื่องจัดการเหตุการณ์เพื่อตอบสนองต่อการโต้ตอบนั้นจริงๆ อย่างไรก็ตาม ในบางกรณีก็อาจเป็นประโยชน์สำหรับการวัดเวลาประมวลผลเหตุการณ์ รวมถึงเวลาก่อนที่จะสามารถแสดงผลเฟรมถัดไปได้

ซึ่งทําได้ด้วย Event Timing API (ซึ่งใช้วัด FID) เนื่องจากจะแสดงการประทับเวลาในวงจรเหตุการณ์เป็นจํานวนหนึ่ง ได้แก่

  • startTime: เวลาที่เบราว์เซอร์ได้รับเหตุการณ์
  • processingStart: เวลาที่เบราว์เซอร์เริ่มประมวลผลตัวแฮนเดิลเหตุการณ์สําหรับเหตุการณ์ได้
  • processingEnd: เวลาที่เบราว์เซอร์เรียกใช้โค้ดแบบพร้อมกันทั้งหมดที่เริ่มต้นจากเครื่องจัดการเหตุการณ์สำหรับเหตุการณ์นี้จนเสร็จสมบูรณ์
  • duration: เวลา (ปัดเศษเป็น 8 มิลลิวินาทีเพื่อเหตุผลด้านความปลอดภัย) ระหว่างเวลาที่เบราว์เซอร์ได้รับเหตุการณ์จนกว่าจะระบายสีเฟรมถัดไปได้หลังจากเรียกใช้โค้ดแบบซิงโครนัสทั้งหมดที่เริ่มต้นจากเครื่องจัดการเหตุการณ์เรียบร้อยแล้ว

ตัวอย่างต่อไปนี้จะแสดงวิธีใช้ค่าเหล่านี้เพื่อสร้างการวัดผลที่กำหนดเอง

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  const po = new PerformanceObserver((entryList) => {
    const firstInput = entryList.getEntries()[0];

    // Measure First Input Delay (FID).
    const firstInputDelay = firstInput.processingStart - firstInput.startTime;

    // Measure the time it takes to run all event handlers
    // Note: this does not include work scheduled asynchronously using
    // methods like `requestAnimationFrame()` or `setTimeout()`.
    const firstInputProcessingTime = firstInput.processingEnd - firstInput.processingStart;

    // Measure the entire duration of the event, from when input is received by
    // the browser until the next frame can be painted after processing all
    // event handlers.
    // Note: similar to above, this value does not include work scheduled
    // asynchronously using `requestAnimationFrame()` or `setTimeout()`.
    // And for security reasons, this value is rounded to the nearest 8ms.
    const firstInputDuration = firstInput.duration;

    // Log these values the console.
    console.log({
      firstInputDelay,
      firstInputProcessingTime,
      firstInputDuration,
    });
  });

  po.observe({type: 'first-input', buffered: true});
} catch (error) {
  // Do nothing if the browser doesn't support this API.
}

API การจับเวลาทรัพยากร

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

  • initiatorType: วิธีดึงข้อมูลทรัพยากร เช่น จากแท็ก <script> หรือ <link> หรือจาก fetch()
  • nextHopProtocol: โปรโตคอลที่ใช้ดึงข้อมูลทรัพยากร เช่น h2 หรือ quic
  • encodedBodySize/decodedBodySize]: ขนาดของทรัพยากรในรูปแบบที่เข้ารหัสหรือถอดรหัส (ตามลำดับ)
  • transferSize: ขนาดของทรัพยากรที่มีการโอนจริงผ่านเครือข่าย เมื่อมีการเติมทรัพยากรผ่านแคช ค่านี้จะน้อยกว่า encodedBodySize มากและในบางกรณีอาจเป็น 0 (หากไม่จำเป็นต้องตรวจสอบแคชอีกครั้ง)

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

ตัวอย่างต่อไปนี้บันทึกทรัพยากรทั้งหมดที่หน้าเว็บขอ และระบุว่าทรัพยากรแต่ละรายการมีการตอบสนองผ่านทางแคชหรือไม่

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // If transferSize is 0, the resource was fulfilled via the cache.
      console.log(entry.name, entry.transferSize === 0);
    }
  });
  // Start listening for `resource` entries to be dispatched.
  po.observe({type: 'resource', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

การสนับสนุนเบราว์เซอร์

  • 57
  • 12
  • 58
  • 15

แหล่งที่มา

Navigation Timing API คล้ายกับ Resource Timing API แต่จะรายงานเฉพาะคำขอการนำทางเท่านั้น รายการประเภท navigation ยังคล้ายกับประเภทรายการ resource ด้วย แต่จะมีข้อมูลเพิ่มเติมบางอย่างสำหรับคำขอการนำทางเท่านั้น (เช่น เมื่อเหตุการณ์ DOMContentLoaded และ load เริ่มทำงาน)

เมตริกหนึ่งที่นักพัฒนาซอฟต์แวร์จำนวนมากติดตามเพื่อทำความเข้าใจเวลาในการตอบกลับของเซิร์ฟเวอร์ (เวลาเป็นไบต์แรก) จะพร้อมใช้งานผ่าน Navigation Timing API โดยเฉพาะเป็นการประทับเวลา responseStart ของรายการ

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // If transferSize is 0, the resource was fulfilled via the cache.
      console.log('Time to first byte', entry.responseStart);
    }
  });
  // Start listening for `navigation` entries to be dispatched.
  po.observe({type: 'navigation', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

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

เวลาเริ่มต้นของโปรแกรมทำงานของบริการสำหรับคำขอการนำทางหนึ่งๆ สามารถกำหนดได้จากเดลต้าระหว่าง entry.responseStart ถึง entry.workerStart

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      console.log('Service Worker startup time:',
          entry.responseStart - entry.workerStart);
    }
  });
  // Start listening for `navigation` entries to be dispatched.
  po.observe({type: 'navigation', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

API เวลาของเซิร์ฟเวอร์

Server Timing API จะช่วยให้คุณส่งข้อมูลการกำหนดเวลาที่เจาะจงคำขอจากเซิร์ฟเวอร์ของคุณไปยังเบราว์เซอร์ผ่านส่วนหัวการตอบกลับได้ ตัวอย่างเช่น คุณระบุระยะเวลาที่ใช้ในการค้นหาข้อมูลในฐานข้อมูลสำหรับคำขอหนึ่งๆ ซึ่งมีประโยชน์ในการแก้ไขข้อบกพร่องของประสิทธิภาพการทำงานที่เกิดจากความช้าของเซิร์ฟเวอร์

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

หากต้องการระบุข้อมูลเวลาของเซิร์ฟเวอร์ในการตอบกลับ ให้ใช้ส่วนหัวการตอบกลับ Server-Timing ดังตัวอย่างต่อไปนี้

HTTP/1.1 200 OK

Server-Timing: miss, db;dur=53, app;dur=47.2

จากนั้นจากหน้าของคุณ คุณจะอ่านข้อมูลนี้ได้ในรายการ resource หรือ navigation จาก Resource Timing และ Navigation Timing API

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Logs all server timing data for this response
      console.log('Server Timing', entry.serverTiming);
    }
  });
  // Start listening for `navigation` entries to be dispatched.
  po.observe({type: 'navigation', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}