การมีเมตริกที่เน้นผู้ใช้ซึ่งวัดได้แบบสากลในเว็บไซต์ใดก็ได้นั้นมีประโยชน์อย่างยิ่ง เมตริกเหล่านี้ช่วยให้คุณทําสิ่งต่อไปนี้ได้
- ทําความเข้าใจว่าผู้ใช้จริงได้รับประสบการณ์การใช้งานเว็บโดยรวมอย่างไร
- เปรียบเทียบเว็บไซต์ของคุณกับเว็บไซต์ของคู่แข่ง
- ติดตามข้อมูลที่เป็นประโยชน์และนําไปใช้ได้จริงในเครื่องมือวิเคราะห์โดยไม่ต้องเขียนโค้ดที่กําหนดเอง
เมตริกสากลเป็นเกณฑ์พื้นฐานที่ดี แต่ในหลายกรณี คุณจะต้องวัดมากกว่าเมตริกเหล่านี้เพื่อบันทึกประสบการณ์การใช้งานเว็บไซต์อย่างเต็มรูปแบบ
เมตริกที่กําหนดเองช่วยให้คุณวัดแง่มุมต่างๆ ของประสบการณ์การใช้งานเว็บไซต์ที่อาจใช้กับเว็บไซต์ของคุณเท่านั้นได้ เช่น
- ระยะเวลาที่แอปหน้าเว็บเดียว (SPA) ใช้ในการเปลี่ยนจาก "หน้า" หนึ่งไปยังอีกหน้าหนึ่ง
- เวลาที่หน้าเว็บแสดงข้อมูลที่ดึงมาจากฐานข้อมูลสําหรับผู้ใช้ที่เข้าสู่ระบบ
- ระยะเวลาที่แอปที่แสดงผลฝั่งเซิร์ฟเวอร์ (SSR) ใช้ในการไฮเดรต
- อัตรา Hit ของแคชสําหรับทรัพยากรที่โหลดโดยผู้เข้าชมที่กลับมา
- เวลาในการตอบสนองของเหตุการณ์การคลิกหรือแป้นพิมพ์ในเกม
API สําหรับวัดเมตริกที่กําหนดเอง
ที่ผ่านมานักพัฒนาเว็บมี API ระดับล่างไม่มากนักในการวัดประสิทธิภาพ ส่งผลให้นักพัฒนาเว็บต้องใช้วิธีแฮ็กเพื่อวัดว่าเว็บไซต์มีประสิทธิภาพดีหรือไม่
ตัวอย่างเช่น คุณสามารถระบุได้ว่าเทรดหลักถูกบล็อกเนื่องจากงาน JavaScript ที่ทำงานเป็นเวลานานหรือไม่โดยเรียกใช้ลูป requestAnimationFrame
และคำนวณค่าต่างระหว่างแต่ละเฟรม หากค่าเดลต้านานกว่าอัตราเฟรมของจอแสดงผลอย่างมีนัยสำคัญ คุณสามารถรายงานเป็นงานที่ใช้เวลานานได้ อย่างไรก็ตาม เราไม่แนะนำให้ใช้วิธีดังกล่าวเนื่องจากวิธีเหล่านี้ส่งผลต่อประสิทธิภาพด้วย (เช่น ทำให้แบตเตอรี่หมด)
กฎข้อแรกของการวัดประสิทธิภาพที่มีประสิทธิภาพคือการดูว่าเทคนิคการวัดประสิทธิภาพไม่ได้เป็นสาเหตุของปัญหาด้านประสิทธิภาพ ดังนั้นสําหรับเมตริกที่กําหนดเองซึ่งคุณวัดในเว็บไซต์ เราขอแนะนําให้ใช้ API รายการใดรายการหนึ่งต่อไปนี้หากเป็นไปได้
Performance Observer API
Performance Observer API เป็นกลไกที่รวบรวมและแสดงข้อมูลจาก API ประสิทธิภาพอื่นๆ ทั้งหมดที่กล่าวถึงในหน้านี้ การทําความเข้าใจข้อมูลดังกล่าวเป็นสิ่งสําคัญต่อการได้ข้อมูลที่ถูกต้อง
คุณสามารถใช้ PerformanceObserver
เพื่อสมัครรับเหตุการณ์ที่เกี่ยวข้องกับประสิทธิภาพแบบไม่โต้ตอบ ซึ่งจะช่วยให้การเรียกกลับ API ทํางานในช่วงไม่มีการใช้งาน ซึ่งหมายความว่าโดยปกติแล้วจะไม่รบกวนประสิทธิภาพของหน้าเว็บ
หากต้องการสร้าง PerformanceObserver
ให้ส่งผ่าน Callback เพื่อเรียกใช้ทุกครั้งที่มีการส่งรายการประสิทธิภาพใหม่ จากนั้นบอกผู้สังเกตการณ์ว่าให้รอรายการประเภทใดโดยใช้เมธอด observe()
ดังนี้
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'});
ส่วนต่อไปนี้แสดงรายการประเภทรายการต่างๆ ทั้งหมดที่พร้อมให้สังเกตการณ์ แต่ในเบราว์เซอร์รุ่นใหม่ คุณสามารถตรวจสอบประเภทรายการที่ใช้ได้ผ่านพร็อพเพอร์ตี้ PerformanceObserver.supportedEntryTypes
แบบคงที่
ดูรายการที่เกิดขึ้นแล้ว
โดยค่าเริ่มต้น ออบเจ็กต์ PerformanceObserver
จะสังเกตการณ์รายการได้เมื่อรายการเกิดขึ้นเท่านั้น ซึ่งอาจทำให้เกิดปัญหาหากคุณต้องการโหลดโค้ดการวิเคราะห์ประสิทธิภาพแบบ Lazy Load เพื่อไม่ให้บล็อกทรัพยากรที่มีลําดับความสําคัญสูงกว่า
หากต้องการดูรายการที่ผ่านมา (หลังจากที่เกิดขึ้นแล้ว) ให้ตั้งค่า Flag buffered
เป็น true
เมื่อเรียกใช้ observe()
เบราว์เซอร์จะรวมรายการที่ผ่านมาจากบัฟเฟอร์รายการประสิทธิภาพในครั้งแรกที่มีการเรียกใช้การเรียกคืน PerformanceObserver
สูงสุดไม่เกินขนาดบัฟเฟอร์สูงสุดสำหรับประเภทนั้น
po.observe({
type: 'some-entry-type',
buffered: true,
});
API ประสิทธิภาพเดิมที่ควรหลีกเลี่ยง
ก่อนที่จะมี Performance Observer API นักพัฒนาแอปสามารถเข้าถึงรายการประสิทธิภาพได้โดยใช้ 3 วิธีต่อไปนี้ที่กําหนดไว้ในออบเจ็กต์ performance
แม้ว่าระบบจะยังคงรองรับ API เหล่านี้ แต่เราไม่แนะนำให้ใช้งาน เนื่องจาก API เหล่านี้ไม่อนุญาตให้คุณรอรับข้อมูลเมื่อมีรายการใหม่เข้ามา นอกจากนี้ API ใหม่หลายรายการ (เช่น largest-contentful-paint
) จะไม่แสดงผ่านออบเจ็กต์ performance
แต่จะแสดงผ่าน PerformanceObserver
เท่านั้น
โปรดหลีกเลี่ยงการใช้เมธอดเหล่านี้ในโค้ดและเปลี่ยนไปใช้ PerformanceObserver
ในอนาคต เว้นแต่ว่าคุณต้องการความเข้ากันได้กับ Internet Explorer โดยเฉพาะ
User Timing 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
// 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});
Long Tasks API
Long Tasks API มีประโยชน์ในการดูว่าเทรดหลักของเบราว์เซอร์ถูกบล็อกนานพอที่จะส่งผลต่ออัตราเฟรมหรือเวลาในการตอบสนองของอินพุตหรือไม่ API จะรายงานงานที่ดำเนินการนานกว่า 50 มิลลิวินาที
เมื่อใดก็ตามที่คุณต้องเรียกใช้โค้ดที่มีราคาแพง หรือโหลดและเรียกใช้สคริปต์ขนาดใหญ่ คุณควรติดตามว่าโค้ดดังกล่าวบล็อกเธรดหลักหรือไม่ อันที่จริงแล้ว เมตริกระดับสูงขึ้นหลายรายการสร้างขึ้นจาก Long Tasks API เอง (เช่น เวลาในการตอบสนอง (TTI) และเวลาในการบล็อกทั้งหมด (TBT))
หากต้องการระบุว่าเกิดงานระยะยาวขึ้นเมื่อใด คุณสามารถใช้ PerformanceObserver และลงทะเบียนเพื่อสังเกตรายการประเภท longtask
ดังนี้
// 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});
Long Animation Frames API
Long Animation Frames API เป็น API เวอร์ชันใหม่ของ Long Tasks API ที่พิจารณาเฟรมที่นาน (ไม่ใช่งานที่นาน) ซึ่งนานกว่า 50 มิลลิวินาที ซึ่งจะแก้ไขข้อบกพร่องบางอย่างของ Long Tasks API รวมถึงการระบุแหล่งที่มาที่ดีขึ้นและขอบเขตที่กว้างขึ้นของความล่าช้าที่อาจทำให้เกิดปัญหา
หากต้องการระบุว่าเฟรมนานเกิดขึ้นเมื่อใด คุณสามารถใช้ PerformanceObserver และลงทะเบียนเพื่อสังเกตรายการประเภท long-animation-frame
ดังนี้
// 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 `long-animation-frame` entries to be dispatched.
po.observe({type: 'long-animation-frame', buffered: true});
Element Timing API
เมตริก Largest Contentful Paint (LCP) มีประโยชน์ในการดูว่ารูปภาพหรือบล็อกข้อความที่ใหญ่ที่สุดแสดงบนหน้าจอเมื่อใด แต่ในบางกรณีคุณต้องการวัดเวลาในการแสดงผลขององค์ประกอบอื่น
สําหรับกรณีเหล่านี้ ให้ใช้ Element Timing API LCP API สร้างขึ้นจาก Element Timing API และเพิ่มการรายงานอัตโนมัติขององค์ประกอบที่มีเนื้อหามากที่สุด แต่คุณยังรายงานเกี่ยวกับองค์ประกอบอื่นๆ ได้ด้วยการเพิ่มแอตทริบิวต์ elementtiming
อย่างชัดเจน และลงทะเบียน PerformanceObserver เพื่อสังเกตประเภทรายการ element
<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->
<script>
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});
</script>
Event Timing API
เมตริก Interaction to Next Paint (INP) จะประเมินการตอบสนองโดยรวมของหน้าเว็บโดยสังเกตการคลิก แตะ และการโต้ตอบด้วยแป้นพิมพ์ทั้งหมดตลอดอายุการใช้งานของหน้าเว็บ INP ของหน้าเว็บมักเป็นการโต้ตอบที่ใช้เวลานานที่สุดตั้งแต่ผู้ใช้เริ่มโต้ตอบจนถึงเวลาที่เบราว์เซอร์แสดงเฟรมถัดไปที่แสดงผลลัพธ์ภาพจากอินพุตของผู้ใช้
เมตริก INP สร้างขึ้นจาก Event Timing API API นี้จะแสดงการประทับเวลาจำนวนหนึ่งที่เกิดขึ้นระหว่างวงจรชีวิตของเหตุการณ์ ซึ่งรวมถึงข้อมูลต่อไปนี้
startTime
: เวลาที่เบราว์เซอร์ได้รับเหตุการณ์processingStart
: เวลาที่เบราว์เซอร์เริ่มประมวลผลเครื่องจัดการเหตุการณ์สําหรับเหตุการณ์ได้processingEnd
: เวลาที่เบราว์เซอร์เรียกใช้โค้ดแบบซิงโครนัสทั้งหมดที่เริ่มต้นจากตัวแฮนเดิลเหตุการณ์สําหรับเหตุการณ์นี้เสร็จสิ้นduration
: เวลา (ปัดเศษเป็น 8 มิลลิวินาทีเพื่อเหตุผลด้านความปลอดภัย) ระหว่างที่เบราว์เซอร์ได้รับเหตุการณ์จนกว่าจะสามารถวาดเฟรมถัดไปได้หลังจากดำเนินการโค้ดแบบซิงค์ทั้งหมดที่เริ่มต้นจากตัวจัดการเหตุการณ์เสร็จแล้ว
ตัวอย่างต่อไปนี้แสดงวิธีใช้ค่าเหล่านี้เพื่อสร้างการวัดผลที่กําหนดเอง
const po = new PerformanceObserver((entryList) => {
// Get the last interaction observed:
const entries = Array.from(entryList.getEntries()).forEach((entry) => {
// Get various bits of interaction data:
const inputDelay = entry.processingStart - entry.startTime;
const processingTime = entry.processingEnd - entry.processingStart;
const presentationDelay = entry.startTime + entry.duration - entry.processingEnd;
const duration = entry.duration;
const eventType = entry.name;
const target = entry.target || "(not set)"
console.log("----- INTERACTION -----");
console.log(`Input delay (ms): ${inputDelay}`);
console.log(`Event handler processing time (ms): ${processingTime}`);
console.log(`Presentation delay (ms): ${presentationDelay}`);
console.log(`Total event duration (ms): ${duration}`);
console.log(`Event type: ${eventType}`);
console.log(target);
});
});
// A durationThreshold of 16ms is necessary to include more
// interactions, since the default is 104ms. The minimum
// durationThreshold is 16ms.
po.observe({type: 'event', buffered: true, durationThreshold: 16});
Resource Timing API
Resource Timing API จะให้ข้อมูลเชิงลึกโดยละเอียดแก่นักพัฒนาซอฟต์แวร์เกี่ยวกับวิธีโหลดทรัพยากรสําหรับหน้าเว็บหนึ่งๆ แม้ว่าชื่อของ API จะบ่งบอกถึงข้อมูลเกี่ยวกับเวลา แต่ข้อมูลที่มีให้ไม่ได้จำกัดอยู่แค่ข้อมูลดังกล่าว (แม้ว่าจะมีข้อมูลเกี่ยวกับเวลาอยู่มาก) ข้อมูลอื่นๆ ที่คุณเข้าถึงได้มีดังนี้
initiatorType
: วิธีที่ดึงข้อมูลทรัพยากร เช่น จากแท็ก<script>
หรือ<link>
หรือจากการเรียกใช้fetch()
nextHopProtocol
: โปรโตคอลที่ใช้ดึงข้อมูล เช่นh2
หรือquic
encodedBodySize
/decodedBodySize]: ขนาดของทรัพยากรในรูปแบบที่เข้ารหัสหรือถอดรหัสแล้ว (ตามลำดับ)transferSize
: ขนาดของทรัพยากรที่โอนผ่านเครือข่ายจริง เมื่อแคชตอบสนองต่อทรัพยากร ค่านี้อาจน้อยกว่าencodedBodySize
มาก และในบางกรณีอาจเป็น 0 (หากไม่จําเป็นต้องตรวจสอบแคชอีกครั้ง)
คุณสามารถใช้พร็อพเพอร์ตี้ transferSize
ของรายการเวลาในการตอบสนองของทรัพยากรเพื่อวัดเมตริกอัตรา Hit ของแคชหรือเมตริกขนาดทรัพยากรที่แคชทั้งหมด ซึ่งมีประโยชน์ในการทำความเข้าใจว่ากลยุทธ์การแคชทรัพยากรส่งผลต่อประสิทธิภาพของผู้เข้าชมซ้ำอย่างไร
ตัวอย่างต่อไปนี้จะบันทึกทรัพยากรทั้งหมดที่หน้าเว็บขอและระบุว่าแคชตอบสนองทรัพยากรแต่ละรายการหรือไม่
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log(entry.name, entry.transferSize === 0);
}
});
// Start listening for `resource` entries to be dispatched.
po.observe({type: 'resource', buffered: true});
Navigation Timing API
Navigation Timing API คล้ายกับ Resource Timing API แต่จะรายงานเฉพาะคำขอไปยังส่วนต่างๆ ประเภทรายการ navigation
ยังคล้ายกับประเภทรายการ resource
อีกด้วย แต่มีข้อมูลเพิ่มเติมเฉพาะสำหรับคำขอการนําทางเท่านั้น (เช่น เมื่อเหตุการณ์ DOMContentLoaded
และ load
เริ่มทํางาน)
เมตริกหนึ่งที่นักพัฒนาซอฟต์แวร์จํานวนมากติดตามเพื่อทําความเข้าใจเวลาในการตอบกลับของเซิร์ฟเวอร์ (Time To First Byte (TTFB)) มีให้บริการโดยใช้ Navigation Timing API โดยเฉพาะการประทับเวลา responseStart
ของรายการ
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log('Time to first byte', entry.responseStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
เมตริกอีกรายการหนึ่งที่นักพัฒนาซอฟต์แวร์ที่ใช้ Service Worker อาจสนใจคือเวลาเริ่มต้นของ Service Worker สําหรับคําขอไปยังส่วนต่างๆ ระยะเวลาที่เบราว์เซอร์ใช้ในการเริ่มเธรด Service Worker ก่อนที่จะเริ่มขัดจังหวะเหตุการณ์การดึงข้อมูล
คุณสามารถระบุเวลาเริ่มต้นของ Service Worker สําหรับคําขอไปยังหน้าเว็บหนึ่งๆ ได้จากค่าต่างระหว่าง entry.responseStart
กับ entry.workerStart
// 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});
Server Timing 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
// 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});