Tìm hiểu những kiến thức cơ bản về cách sử dụng API Điều hướng và API Thời gian tải tài nguyên để đánh giá hiệu suất tải trong thực tế.
Xuất bản: Ngày 8 tháng 10 năm 2021
Nếu đã sử dụng tính năng điều tiết kết nối trong Bảng điều khiển mạng của công cụ cho nhà phát triển trình duyệt (hoặc Lighthouse trong Chrome) để đánh giá hiệu suất tải, thì bạn sẽ biết những công cụ đó thuận tiện như thế nào cho việc điều chỉnh hiệu suất. Bạn có thể nhanh chóng đo lường tác động của việc tối ưu hoá hiệu suất với tốc độ kết nối cơ sở ổn định và nhất quán. Vấn đề duy nhất là đây là kiểm thử tổng hợp, tạo ra dữ liệu phòng thí nghiệm chứ không phải dữ liệu thực tế.
Thử nghiệm nhân tạo không phải là xấu, nhưng không thể hiện được tốc độ tải trang web của bạn đối với người dùng thực. Điều đó đòi hỏi dữ liệu thực tế mà bạn có thể thu thập từ API Thời gian điều hướng và API Thời gian tài nguyên.
Các API giúp bạn đánh giá hiệu suất tải trong thực tế
Navigation Timing và Resource Timing là 2 API tương tự nhau có nhiều điểm trùng lặp đáng kể, đo lường 2 điều riêng biệt:
- Thời gian điều hướng đo lường tốc độ của các yêu cầu đối với tài liệu HTML (tức là các yêu cầu điều hướng).
- Thời gian tài nguyên đo lường tốc độ của các yêu cầu đối với tài nguyên phụ thuộc vào tài liệu, chẳng hạn như CSS, JavaScript, hình ảnh và các loại tài nguyên khác.
Các API này hiển thị dữ liệu của chúng trong vùng đệm mục hiệu suất. Bạn có thể truy cập vào vùng đệm này trong trình duyệt bằng JavaScript. Có nhiều cách để truy vấn một vùng đệm hiệu suất, nhưng cách phổ biến là sử dụng performance.getEntriesByType:
// Get Navigation Timing entries:
performance.getEntriesByType('navigation');
// Get Resource Timing entries:
performance.getEntriesByType('resource');
performance.getEntriesByType chấp nhận một chuỗi mô tả loại mục nhập mà bạn muốn truy xuất từ vùng đệm mục nhập hiệu suất. 'navigation' và 'resource' lần lượt truy xuất thời gian cho API Navigation Timing và Resource Timing.
Lượng thông tin mà các API này cung cấp có thể rất lớn, nhưng chúng là chìa khoá để bạn đo lường hiệu suất tải trong thực tế, vì bạn có thể thu thập những thông tin về thời gian này từ người dùng khi họ truy cập vào trang web của bạn.
Vòng đời và thời gian của một yêu cầu mạng
Việc thu thập và phân tích thời gian điều hướng và tài nguyên giống như khảo cổ học ở chỗ bạn đang tái tạo vòng đời ngắn ngủi của một yêu cầu mạng sau khi yêu cầu đó được thực hiện. Đôi khi, việc hình dung các khái niệm sẽ giúp ích, và khi có các yêu cầu về mạng, công cụ dành cho nhà phát triển của trình duyệt có thể giúp bạn.
Yêu cầu mạng có các giai đoạn riêng biệt, chẳng hạn như tra cứu DNS, thiết lập kết nối, thương lượng TLS và các nguồn độ trễ khác. Các thời gian này được biểu thị dưới dạng DOMHighResTimestamp. Tuỳ thuộc vào trình duyệt, độ chi tiết của thời gian có thể xuống đến mức vi giây hoặc được làm tròn lên đến mili giây. Bạn nên xem xét kỹ lưỡng các giai đoạn này và mối quan hệ giữa chúng với Navigation Timing (Thời gian điều hướng) và Resource Timing (Thời gian tài nguyên).
tra cứu DNS
Khi người dùng truy cập vào một URL, Hệ thống tên miền (DNS) sẽ được truy vấn để chuyển đổi một miền thành địa chỉ IP. Quá trình này có thể mất nhiều thời gian, thậm chí bạn sẽ muốn đo lường thời gian này trên thực tế. Navigation Timing (Thời gian điều hướng) và Resource Timing (Thời gian tài nguyên) cho biết 2 thời gian liên quan đến DNS:
domainLookupStartlà thời điểm bắt đầu tra cứu DNS.domainLookupEndlà thời điểm kết thúc quá trình tra cứu DNS.
Bạn có thể tính tổng thời gian tra cứu DNS bằng cách trừ chỉ số bắt đầu cho chỉ số kết thúc:
// Measuring DNS lookup time
const [pageNav] = performance.getEntriesByType('navigation');
const totalLookupTime = pageNav.domainLookupEnd - pageNav.domainLookupStart;
Đàm phán kết nối
Một yếu tố khác góp phần vào hiệu suất tải là quá trình thương lượng kết nối, tức là độ trễ phát sinh khi kết nối với một máy chủ web. Nếu có HTTPS, quy trình này cũng sẽ bao gồm thời gian thương lượng TLS. Giai đoạn kết nối bao gồm 3 thời điểm:
connectStartlà khi trình duyệt bắt đầu mở một kết nối đến máy chủ web.secureConnectionStartđánh dấu thời điểm ứng dụng bắt đầu thương lượng TLS.connectEndlà khi kết nối đến máy chủ web đã được thiết lập.
Đo lường tổng thời gian kết nối tương tự như đo lường tổng thời gian tra cứu DNS: bạn trừ thời gian bắt đầu cho thời gian kết thúc. Tuy nhiên, có thêm một thuộc tính secureConnectionStart có thể là 0 nếu không dùng HTTPS hoặc nếu kết nối là liên tục. Nếu muốn đo thời gian thương lượng TLS, bạn cần lưu ý những điều sau:
// 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;
}
Sau khi quá trình tra cứu DNS và thương lượng kết nối kết thúc, thời gian liên quan đến việc tìm nạp tài liệu và các tài nguyên phụ thuộc của tài liệu sẽ bắt đầu.
Yêu cầu và phản hồi
Hiệu suất tải bị ảnh hưởng bởi 2 loại yếu tố:
- Yếu tố bên ngoài: Đây là những yếu tố như độ trễ và băng thông. Ngoài việc chọn một công ty lưu trữ và có thể là một CDN, chúng ta (hầu hết) không thể kiểm soát các yếu tố này vì người dùng có thể truy cập vào web từ mọi nơi.
- Yếu tố nội tại: Đây là những yếu tố như cấu trúc phía máy chủ và phía máy khách, cũng như kích thước tài nguyên và khả năng tối ưu hoá cho những yếu tố đó, nằm trong tầm kiểm soát của chúng tôi.
Cả hai loại yếu tố này đều ảnh hưởng đến hiệu suất tải. Thời gian liên quan đến những yếu tố này là rất quan trọng, vì chúng mô tả thời gian cần thiết để tải tài nguyên xuống. Cả Navigation Timing (Thời gian điều hướng) và Resource Timing (Thời gian tài nguyên) đều mô tả hiệu suất tải bằng các chỉ số sau:
fetchStartđánh dấu thời điểm trình duyệt bắt đầu tìm nạp một tài nguyên (Thời gian tải tài nguyên) hoặc một tài liệu cho yêu cầu điều hướng (Thời gian điều hướng). Điều này xảy ra trước yêu cầu thực tế và là thời điểm trình duyệt kiểm tra bộ nhớ đệm (ví dụ: các phiên bản HTTP vàCache).workerStartđánh dấu thời điểm một yêu cầu bắt đầu được xử lý trong trình xử lý sự kiệnfetchcủa trình chạy dịch vụ. Giá trị này sẽ là0khi không có trình chạy dịch vụ nào kiểm soát trang hiện tại.requestStartlà khi trình duyệt đưa ra yêu cầu.responseStartlà khi byte đầu tiên của phản hồi đến.responseEndlà thời điểm byte cuối cùng của phản hồi đến.
Những thời gian này cho phép bạn đo lường nhiều khía cạnh của hiệu suất tải, chẳng hạn như tra cứu bộ nhớ đệm trong một trình chạy dịch vụ và thời gian tải xuống:
// 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;
}
Bạn cũng có thể đo lường các khía cạnh khác của độ trễ yêu cầu và phản hồi:
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;
Các phép đo khác mà bạn có thể thực hiện
Navigation Timing (Thời gian điều hướng) và Resource Timing (Thời gian tài nguyên) hữu ích hơn những gì các ví dụ trước đó trình bày. Sau đây là một số tình huống khác có thời gian liên quan mà bạn nên tìm hiểu:
- Lệnh chuyển hướng trang: Lệnh chuyển hướng là một nguồn bị bỏ qua gây ra độ trễ gia tăng, đặc biệt là chuỗi chuyển hướng. Độ trễ được thêm vào theo nhiều cách, chẳng hạn như các bước chuyển từ HTTP sang HTTPS, cũng như các lệnh chuyển hướng 302/301 không được lưu vào bộ nhớ đệm. Thời gian
redirectStart,redirectEndvàredirectCountrất hữu ích trong việc đánh giá độ trễ chuyển hướng. - Tải tài liệu xuống: Trong các trang chạy mã trong trình xử lý sự kiện
unload, trình duyệt phải thực thi mã đó thì mới có thể chuyển đến trang tiếp theo.unloadEventStartvàunloadEventEndđo lường việc huỷ tải tài liệu. - Xử lý tài liệu: Thời gian xử lý tài liệu có thể không đáng kể, trừ phi trang web của bạn gửi các tải trọng HTML rất lớn. Nếu đây là trường hợp của bạn, thì bạn có thể quan tâm đến thời gian
domInteractive,domContentLoadedEventStart,domContentLoadedEventEndvàdomComplete.
Cách lấy thời gian trong mã
Tất cả các ví dụ đã trình bày cho đến nay đều sử dụng performance.getEntriesByType, nhưng có những cách khác để truy vấn vùng đệm mục hiệu suất, chẳng hạn như performance.getEntriesByName và performance.getEntries. Những phương thức này phù hợp khi bạn chỉ cần phân tích sơ bộ. Tuy nhiên, trong những trường hợp khác, chúng có thể gây ra quá nhiều hoạt động cho luồng chính bằng cách lặp lại một số lượng lớn các mục, hoặc thậm chí liên tục thăm dò bộ nhớ đệm hiệu suất để tìm các mục mới.
Bạn nên sử dụng PerformanceObserver để thu thập các mục từ vùng đệm mục hiệu suất. PerformanceObserver theo dõi các mục hiệu suất và cung cấp các mục đó khi chúng được thêm vào vùng đệm:
// 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
});
Phương thức thu thập thời gian này có thể gây khó chịu khi so sánh với việc truy cập trực tiếp vào vùng đệm mục hiệu suất, nhưng tốt hơn là nên liên kết luồng chính với công việc không phục vụ mục đích quan trọng và dành cho người dùng.
Cách gọi điện về nhà
Sau khi thu thập tất cả các thời gian cần thiết, bạn có thể gửi chúng đến một điểm cuối để phân tích thêm. Có hai cách để thực hiện việc này: dùng navigator.sendBeacon hoặc fetch với lựa chọn keepalive được đặt. Cả hai phương thức này đều sẽ gửi yêu cầu đến một điểm cuối được chỉ định theo cách không chặn và yêu cầu sẽ được xếp hàng theo cách tồn tại lâu hơn phiên trang hiện tại nếu cần:
// 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);
}
Trong ví dụ này, chuỗi JSON sẽ xuất hiện trong tải trọng POST mà bạn có thể giải mã, xử lý và lưu trữ trong một phần phụ trợ ứng dụng nếu cần.
Kết luận
Sau khi thu thập được các chỉ số, bạn sẽ phải tìm ra cách phân tích dữ liệu của trường đó. Khi phân tích dữ liệu thực tế, bạn cần tuân theo một số quy tắc chung để đảm bảo bạn rút ra được những kết luận có ý nghĩa:
- Tránh sử dụng số liệu trung bình vì số liệu này không đại diện cho trải nghiệm của bất kỳ người dùng nào và có thể bị ảnh hưởng bởi các giá trị ngoại lệ.
- Dựa vào phân vị. Trong các tập dữ liệu về chỉ số hiệu suất dựa trên thời gian, giá trị càng thấp càng tốt. Điều này có nghĩa là khi ưu tiên các phân vị thấp, bạn chỉ chú ý đến những trải nghiệm nhanh nhất.
- Ưu tiên các giá trị đuôi dài. Khi ưu tiên những trải nghiệm ở phân vị thứ 75 trở lên, bạn đang tập trung vào những trải nghiệm chậm nhất.
Hướng dẫn này không phải là một tài nguyên đầy đủ về Navigation Timing hoặc Resource Timing, mà chỉ là một điểm khởi đầu. Dưới đây là một số tài nguyên bổ sung có thể hữu ích cho bạn:
- Navigation Timing Spec (Thông số kỹ thuật về thời gian điều hướng).
- Resource Timing Spec (Thông số kỹ thuật về thời gian tải tài nguyên).
- ResourceTiming trong thực tế.
- Navigation Timing API (MDN)
- Resource Timing API (MDN)
Với những API này và dữ liệu mà chúng cung cấp, bạn sẽ có thêm thông tin để hiểu rõ trải nghiệm của người dùng thực tế về hiệu suất tải. Nhờ đó, bạn sẽ tự tin hơn khi chẩn đoán và giải quyết các vấn đề về hiệu suất tải trong thực tế.