ดูข้อมูลพื้นฐานเกี่ยวกับการใช้ Navigation และ Resource Timing API เพื่อประเมินประสิทธิภาพการโหลดในสภาพแวดล้อมจริง
เผยแพร่เมื่อวันที่ 8 ตุลาคม 2021
หากคุณเคยใช้การควบคุมปริมาณการเชื่อมต่อในแผงเครือข่ายในเครื่องมือสำหรับนักพัฒนาเว็บของเบราว์เซอร์ (หรือ Lighthouse ใน Chrome) เพื่อประเมินประสิทธิภาพการโหลด คุณจะทราบว่าเครื่องมือเหล่านั้นสะดวกเพียงใดสำหรับการปรับประสิทธิภาพ คุณสามารถวัดผลกระทบของการเพิ่มประสิทธิภาพได้อย่างรวดเร็วด้วยความเร็วการเชื่อมต่อพื้นฐานที่สม่ำเสมอและเสถียร ปัญหาเดียวคือการทดสอบนี้เป็นการทดสอบแบบสังเคราะห์ ซึ่งให้ข้อมูลในห้องทดลอง ไม่ใช่ข้อมูลในสภาพแวดล้อมจริง
การทดสอบแบบสังเคราะห์ไม่ได้ แย่ ในตัวของมันเอง แต่ไม่ได้แสดงถึงความเร็วในการโหลดเว็บไซต์สำหรับผู้ใช้จริง คุณจึงต้องใช้ข้อมูลในสภาพแวดล้อมจริง ซึ่งคุณสามารถรวบรวมได้จาก Navigation Timing และ Resource Timing API
API ที่ช่วยคุณประเมินประสิทธิภาพการโหลดในสภาพแวดล้อมจริง
Navigation Timing และ Resource Timing เป็น API 2 รายการที่คล้ายกันและมีฟังก์ชันการทำงานที่ทับซ้อนกันอย่างมาก ซึ่งวัดสิ่งต่างๆ 2 อย่างที่แตกต่างกัน ดังนี้
- Navigation Timing วัดความเร็วของคำขอเอกสาร HTML (นั่นคือ คำขอการนำทาง)
- Resource Timing วัดความเร็วของคำขอทรัพยากรที่ขึ้นอยู่กับเอกสาร เช่น CSS, JavaScript, รูปภาพ และทรัพยากรประเภทอื่นๆ
API เหล่านี้แสดงข้อมูลใน บัฟเฟอร์รายการประสิทธิภาพ ซึ่งเข้าถึงได้ในเบราว์เซอร์ด้วย JavaScript คุณสามารถค้นหาบัฟเฟอร์ประสิทธิภาพได้หลายวิธี แต่วิธีที่ใช้กันโดยทั่วไปคือการใช้ performance.getEntriesByType
// Get Navigation Timing entries:
performance.getEntriesByType('navigation');
// Get Resource Timing entries:
performance.getEntriesByType('resource');
performance.getEntriesByType รับสตริงที่อธิบายประเภทรายการที่คุณต้องการดึงข้อมูลจากบัฟเฟอร์รายการประสิทธิภาพ 'navigation' และ 'resource' จะดึงข้อมูลเวลาสำหรับ Navigation Timing และ Resource Timing API ตามลำดับ
ข้อมูลจำนวนมากที่ API เหล่านี้ให้มาอาจทำให้คุณสับสนได้ แต่ API เหล่านี้เป็นกุญแจสำคัญในการวัดประสิทธิภาพการโหลดในสภาพแวดล้อมจริง เนื่องจากคุณสามารถรวบรวมข้อมูลเวลาเหล่านี้จากผู้ใช้ขณะที่เข้าชมเว็บไซต์
วงจรและเวลาของคำขอเครือข่าย
การรวบรวมและวิเคราะห์เวลาการนำทางและเวลาทรัพยากรคล้ายกับการขุดค้นทางโบราณคดีตรงที่คุณกำลังสร้างวงจรชีวิตชั่วคราวของคำขอเครือข่ายขึ้นมาใหม่หลังจากเหตุการณ์ผ่านไปแล้ว บางครั้งการแสดงภาพแนวคิดอาจช่วยได้ และในกรณีของคำขอเครือข่าย เครื่องมือสำหรับนักพัฒนาเว็บของเบราว์เซอร์จะช่วยคุณได้
วงจรชีวิตของคำขอเครือข่ายมีหลายระยะที่แตกต่างกัน เช่น DNS Lookup, การสร้างการเชื่อมต่อ, การเจรจาต่อรอง TLS และแหล่งที่มาอื่นๆ ของเวลาในการตอบสนอง เวลาเหล่านี้แสดงเป็น DOMHighResTimestamp ความละเอียดของเวลาอาจละเอียดถึงระดับไมโครวินาทีหรือปัดขึ้นเป็นมิลลิวินาที ทั้งนี้ขึ้นอยู่กับเบราว์เซอร์ คุณจะต้องตรวจสอบระยะเหล่านี้โดยละเอียด รวมถึงความสัมพันธ์กับ Navigation Timing และ Resource Timing
DNS Lookup
เมื่อผู้ใช้ไปที่ URL ระบบจะค้นหา Domain Name System (DNS) เพื่อแปลโดเมนเป็นที่อยู่ IP กระบวนการนี้อาจใช้เวลานาน ซึ่งเป็นเวลาที่คุณจะต้องวัดในสภาพแวดล้อมจริง Navigation Timing และ Resource Timing แสดงเวลา 2 รายการที่เกี่ยวข้องกับ DNS ดังนี้
domainLookupStartคือเวลาที่ DNS Lookup เริ่มต้นdomainLookupEndคือเวลาที่ DNS Lookup สิ้นสุด
คุณสามารถคำนวณเวลารวมของ DNS Lookup ได้โดยลบเมตริกเริ่มต้นออกจากเมตริกสิ้นสุด ดังนี้
// Measuring DNS lookup time
const [pageNav] = performance.getEntriesByType('navigation');
const totalLookupTime = pageNav.domainLookupEnd - pageNav.domainLookupStart;
การเจรจาต่อรองการเชื่อมต่อ
อีกปัจจัยหนึ่งที่ส่งผลต่อประสิทธิภาพการโหลดคือการเจรจาต่อรองการเชื่อมต่อ ซึ่งเป็นเวลาในการตอบสนองที่เกิดขึ้นเมื่อเชื่อมต่อกับเว็บเซิร์ฟเวอร์ หากเกี่ยวข้องกับ HTTPS กระบวนการนี้จะรวมเวลาการเจรจาต่อรอง TLS ด้วย ระยะการเชื่อมต่อประกอบด้วยเวลา 3 รายการ ดังนี้
connectStartคือเวลาที่เบราว์เซอร์เริ่มเปิดการเชื่อมต่อกับเว็บเซิร์ฟเวอร์secureConnectionStartระบุเวลาที่ไคลเอ็นต์เริ่มการเจรจาต่อรอง TLSconnectEndคือเวลาที่สร้างการเชื่อมต่อกับเว็บเซิร์ฟเวอร์แล้ว
การวัดเวลารวมของการเชื่อมต่อจะคล้ายกับการวัดเวลารวมของ DNS Lookup โดยคุณจะลบเวลาเริ่มต้นออกจากเวลาสิ้นสุด อย่างไรก็ตาม มีพร็อพเพอร์ตี้ secureConnectionStart เพิ่มเติมซึ่งอาจมีค่าเป็น 0 หากไม่ได้ใช้ HTTPS หรือ หากการเชื่อมต่อเป็นแบบถาวร หากต้องการวัดเวลาการเจรจาต่อรอง TLS คุณจะต้องคำนึงถึงสิ่งต่อไปนี้
// Quantifying total connection time
const [pageNav] = performance.getEntriesByType('navigation');
const connectionTime = pageNav.connectEnd - pageNav.connectStart;
let tlsTime = 0; // <-- Assume 0 to start with
// Was there TLS negotiation?
if (pageNav.secureConnectionStart > 0) {
// Awesome! Calculate it!
tlsTime = pageNav.connectEnd - pageNav.secureConnectionStart;
}
เมื่อ DNS Lookup และการเจรจาต่อรองการเชื่อมต่อสิ้นสุดลง เวลาที่เกี่ยวข้องกับการดึงข้อมูลเอกสารและทรัพยากรที่ขึ้นอยู่กับเอกสารจะเริ่มทำงาน
คำขอและการตอบกลับ
ประสิทธิภาพการโหลดได้รับผลกระทบจากปัจจัย 2 ประเภท ดังนี้
- ปัจจัยภายนอก: ปัจจัยเหล่านี้ ได้แก่ เวลาในการตอบสนองและแบนด์วิดท์ นอกเหนือจากการเลือกบริษัทโฮสติ้งและอาจเลือก CDN แล้ว ปัจจัยเหล่านี้ส่วนใหญ่จะอยู่นอกเหนือการควบคุมของเรา เนื่องจากผู้ใช้สามารถเข้าถึงเว็บได้จากทุกที่
- ปัจจัยภายใน: ปัจจัยเหล่านี้ ได้แก่ สถาปัตยกรรมฝั่งเซิร์ฟเวอร์และฝั่งไคลเอ็นต์ รวมถึงขนาดทรัพยากรและความสามารถของเราในการเพิ่มประสิทธิภาพสำหรับสิ่งเหล่านั้น ซึ่งอยู่ในความควบคุมของเรา
ปัจจัยทั้ง 2 ประเภทส่งผลต่อประสิทธิภาพการโหลด เวลาที่เกี่ยวข้องกับปัจจัยเหล่านี้มีความสำคัญอย่างยิ่ง เนื่องจากอธิบายระยะเวลาที่ใช้ในการดาวน์โหลดทรัพยากร ทั้ง Navigation Timing และ Resource Timing อธิบายประสิทธิภาพการโหลดด้วยเมตริกต่อไปนี้
fetchStartระบุเวลาที่เบราว์เซอร์เริ่มดึงข้อมูลทรัพยากร (Resource Timing) หรือเอกสารสำหรับคำขอการนำทาง (Navigation Timing) ซึ่งจะเกิดขึ้นก่อนคำขอจริง และเป็นจุดที่เบราว์เซอร์กำลังตรวจสอบแคช (เช่น อินสแตนซ์ HTTP และCacheอินสแตนซ์)workerStartระบุเวลาที่เริ่มจัดการคำขอภายในตัวแฮนเดิลเหตุการณ์fetchของ Service Worker ค่าจะเป็น0เมื่อไม่มี Service Worker ควบคุมหน้าปัจจุบันrequestStartคือเวลาที่เบราว์เซอร์ส่งคำขอresponseStartคือเวลาที่ไบต์แรกของการตอบกลับมาถึงresponseEndคือเวลาที่ไบต์สุดท้ายของการตอบกลับมาถึง
เวลาเหล่านี้ช่วยให้คุณวัดประสิทธิภาพการโหลดได้หลายแง่มุม เช่น การค้นหาแคชภายใน Service Worker และ เวลาดาวน์โหลด ดังนี้
// Cache seek plus response time of the current document
const [pageNav] = performance.getEntriesByType('navigation');
const fetchTime = pageNav.responseEnd - pageNav.fetchStart;
// Service worker time plus response time
let workerTime = 0;
if (pageNav.workerStart > 0) {
workerTime = pageNav.responseEnd - pageNav.workerStart;
}
นอกจากนี้ คุณยังวัดเวลาในการตอบสนองอื่นๆ ของคำขอและการตอบกลับได้ด้วย ดังนี้
const [pageNav] = performance.getEntriesByType('navigation');
// Request time only (excluding redirects, DNS, and connection/TLS time)
const requestTime = pageNav.responseStart - pageNav.requestStart;
// Response time only (download)
const responseTime = pageNav.responseEnd - pageNav.responseStart;
// Request + response time
const requestResponseTime = pageNav.responseEnd - pageNav.requestStart;
การวัดอื่นๆ ที่คุณทำได้
Navigation Timing และ Resource Timing มีประโยชน์มากกว่าที่ตัวอย่างก่อนหน้านี้ระบุไว้ ต่อไปนี้คือสถานการณ์อื่นๆ ที่มีเวลาที่เกี่ยวข้องซึ่งอาจคุ้มค่าที่จะสำรวจ
- การเปลี่ยนเส้นทางของหน้าเว็บ: การเปลี่ยนเส้นทางเป็นแหล่งที่มาที่ถูกมองข้ามซึ่งทำให้เกิดเวลาในการตอบสนองเพิ่มขึ้น โดยเฉพาะการเปลี่ยนเส้นทางแบบเป็นห่วงโซ่ เวลาในการตอบสนองจะเพิ่มขึ้นในหลายวิธี เช่น การข้ามจาก HTTP ไปยัง HTTPS รวมถึงการเปลี่ยนเส้นทาง 302/301 ที่ไม่ได้แคช เวลา
redirectStart,redirectEndและredirectCountมีประโยชน์ในการประเมินเวลาในการตอบสนองของการเปลี่ยนเส้นทาง - การยกเลิกการโหลดเอกสาร: ในหน้าที่เรียกใช้โค้ดในตัวแฮนเดิลเหตุการณ์
unloadเบราว์เซอร์จะต้องเรียกใช้โค้ดนั้นก่อนจึงจะไปยังหน้าถัดไปได้unloadEventStartและunloadEventEndจะวัดการยกเลิกการโหลดเอกสาร - การประมวลผลเอกสาร: เวลาการประมวลผลเอกสารอาจไม่สำคัญเว้นแต่เว็บไซต์ของคุณจะส่งเพย์โหลด HTML ขนาดใหญ่มาก หากสถานการณ์ของคุณเป็นเช่นนี้ เวลา
domInteractive,domContentLoadedEventStart,domContentLoadedEventEndและdomCompleteอาจเป็นประโยชน์
วิธีรับเวลาในโค้ด
ตัวอย่างทั้งหมดที่แสดงจนถึงตอนนี้ใช้ performance.getEntriesByType แต่คุณสามารถค้นหาบัฟเฟอร์รายการประสิทธิภาพได้ด้วยวิธีอื่นๆ เช่น performance.getEntriesByName และ performance.getEntries วิธีการเหล่านี้เหมาะสำหรับการวิเคราะห์แบบเบาๆ เท่านั้น อย่างไรก็ตาม ในสถานการณ์อื่นๆ วิธีการเหล่านี้อาจทำให้เกิดงานของเธรดหลักมากเกินไปโดยการวนซ้ำรายการจำนวนมาก หรือแม้แต่การโพลบัฟเฟอร์ประสิทธิภาพซ้ำๆ เพื่อค้นหารายการใหม่
วิธีที่แนะนำสำหรับการรวบรวมรายการจากบัฟเฟอร์รายการประสิทธิภาพคือการใช้ PerformanceObserver PerformanceObserver จะรอฟังรายการประสิทธิภาพและแสดงรายการเหล่านั้นเมื่อมีการเพิ่มลงในบัฟเฟอร์
// Create the performance observer:
const perfObserver = new PerformanceObserver((observedEntries) => {
// Get all resource entries collected so far:
const entries = observedEntries.getEntries();
// Iterate over entries:
for (let i = 0; i < entries.length; i++) {
// Do the work!
}
});
// Run the observer for Navigation Timing entries:
perfObserver.observe({
type: 'navigation',
buffered: true
});
// Run the observer for Resource Timing entries:
perfObserver.observe({
type: 'resource',
buffered: true
});
วิธีการรวบรวมเวลาแบบนี้อาจดูแปลกเมื่อเทียบกับการเข้าถึงบัฟเฟอร์รายการประสิทธิภาพโดยตรง แต่ก็ดีกว่าการผูกเธรดหลักไว้กับงานที่ไม่ได้มีวัตถุประสงค์ที่สำคัญและมุ่งเน้นผู้ใช้
วิธีส่งข้อมูลกลับ
เมื่อรวบรวมเวลาทั้งหมดที่ต้องการแล้ว คุณสามารถส่งเวลาเหล่านั้นไปยังปลายทางเพื่อทำการวิเคราะห์เพิ่มเติมได้ คุณทำได้ 2 วิธี ได้แก่ ใช้ navigator.sendBeacon หรือ fetch ที่ตั้งค่าตัวเลือก keepalive ทั้ง 2 วิธีจะส่งคำขอไปยังปลายทางที่ระบุในลักษณะที่ไม่บล็อก และระบบจะจัดคิวคำขอในลักษณะที่คำขอจะยังคงอยู่ต่อไปได้แม้ว่าเซสชันหน้าปัจจุบันจะสิ้นสุดลงแล้วก็ตาม หากจำเป็น
// Check for navigator.sendBeacon support:
if ('sendBeacon' in navigator) {
// Caution: If you have lots of performance entries, don't
// do this. This is an example for illustrative purposes.
const data = JSON.stringify(performance.getEntries());
// Send the data!
navigator.sendBeacon('/analytics', data);
}
ในตัวอย่างนี้ สตริง JSON จะมาถึงในเพย์โหลด POST ซึ่งคุณสามารถถอดรหัส ประมวลผล และจัดเก็บไว้ในแบ็กเอนด์ของแอปพลิเคชันได้ตามต้องการ
บทสรุป
เมื่อรวบรวมเมตริกแล้ว คุณจะต้องหาวิธีวิเคราะห์ข้อมูลในสภาพแวดล้อมจริง เมื่อวิเคราะห์ข้อมูลในสภาพแวดล้อมจริง คุณต้องปฏิบัติตามกฎทั่วไป 2-3 ข้อเพื่อให้แน่ใจว่าคุณได้ข้อสรุปที่มีความหมาย ดังนี้
- หลีกเลี่ยงค่าเฉลี่ย เนื่องจากค่าเฉลี่ยไม่ได้แสดงถึงประสบการณ์ของผู้ใช้รายใดรายหนึ่ง และอาจได้รับผลกระทบจากค่าผิดปกติ
- ใช้เปอร์เซ็นไทล์ ในชุดข้อมูลเมตริกประสิทธิภาพตามเวลา ค่าที่ต่ำกว่าจะดีกว่า ซึ่งหมายความว่าเมื่อคุณจัดลำดับความสำคัญของเปอร์เซ็นไทล์ต่ำ คุณจะพิจารณาเฉพาะประสบการณ์ที่เร็วที่สุด
- จัดลำดับความสำคัญของค่าหางยาว เมื่อคุณจัดลำดับความสำคัญของประสบการณ์ที่เปอร์เซ็นไทล์ที่ 75 ขึ้นไป คุณจะมุ่งเน้นไปที่ประสบการณ์ที่ช้าที่สุด ซึ่งเป็นสิ่งที่ควรทำ
คู่มือนี้ไม่ได้มีวัตถุประสงค์เพื่อเป็นแหล่งข้อมูลที่ครอบคลุมเกี่ยวกับ Navigation หรือ Resource Timing แต่เป็นจุดเริ่มต้น ต่อไปนี้คือแหล่งข้อมูลเพิ่มเติมที่อาจเป็นประโยชน์
- ข้อกำหนด Navigation Timing
- ข้อกำหนด Resource Timing
- ResourceTiming ในทางปฏิบัติ
- Navigation Timing API (MDN)
- Resource Timing API (MDN)
API เหล่านี้และข้อมูลที่ API ให้มาจะช่วยให้คุณเข้าใจได้ดียิ่งขึ้นว่าผู้ใช้จริงได้รับประสบการณ์การโหลดอย่างไร ซึ่งจะช่วยให้คุณมั่นใจมากขึ้นในการวินิจฉัยและแก้ไขปัญหาประสิทธิภาพการโหลดในสภาพแวดล้อมจริง