Việc có các chỉ số tập trung vào người dùng mà bạn có thể đo lường trên mọi trang web cụ thể đều mang lại nhiều giá trị. Những chỉ số này giúp bạn:
- Tìm hiểu cách người dùng thực trải nghiệm toàn bộ web.
- So sánh trang web của bạn với trang web của đối thủ cạnh tranh.
- Theo dõi dữ liệu hữu ích và có thể hành động trong các công cụ phân tích mà không cần viết mã tuỳ chỉnh.
Các chỉ số chung cung cấp một đường cơ sở tốt, nhưng trong nhiều trường hợp, bạn cần đo lường nhiều chỉ số hơn những chỉ số này để nắm bắt toàn bộ trải nghiệm cho trang web cụ thể của mình.
Chỉ số tuỳ chỉnh cho phép bạn đo lường những khía cạnh trong trải nghiệm trên trang web của mình mà có thể chỉ áp dụng cho trang web của bạn, chẳng hạn như:
- Khoảng thời gian cần thiết để một ứng dụng trang đơn (SPA) chuyển đổi từ "trang" này sang "trang" khác.
- Thời gian cần thiết để một trang hiển thị dữ liệu được tìm nạp từ cơ sở dữ liệu cho người dùng đã đăng nhập.
- Thời gian cần thiết để một ứng dụng kết xuất phía máy chủ (SSR) khởi động.
- Tỷ lệ truy cập bộ nhớ đệm cho các tài nguyên do khách truy cập cũ tải.
- Độ trễ của sự kiện nhấp hoặc sự kiện bàn phím trong một trò chơi.
API để đo lường chỉ số tuỳ chỉnh
Trước đây, các nhà phát triển web không có nhiều API cấp thấp để đo lường hiệu suất. Do đó, họ phải dùng đến các giải pháp tạm thời để đo lường xem một trang web có hoạt động hiệu quả hay không.
Ví dụ: bạn có thể xác định xem luồng chính có bị chặn do các tác vụ JavaScript chạy trong thời gian dài hay không bằng cách chạy một vòng lặp requestAnimationFrame và tính toán mức chênh lệch giữa mỗi khung hình. Nếu delta dài hơn đáng kể so với tốc độ khung hình của màn hình, bạn có thể báo cáo đó là một tác vụ dài. Tuy nhiên, bạn không nên dùng những cách này vì chúng thực sự ảnh hưởng đến hiệu suất (ví dụ: làm hao pin).
Quy tắc đầu tiên để đo lường hiệu suất một cách hiệu quả là đảm bảo rằng các kỹ thuật đo lường hiệu suất của bạn không gây ra các vấn đề về hiệu suất. Vì vậy, đối với mọi chỉ số tuỳ chỉnh mà bạn đo lường trên trang web của mình, tốt nhất là bạn nên sử dụng một trong các API sau (nếu có thể).
Performance Observer API
Performance Observer API là cơ chế thu thập và hiển thị dữ liệu từ tất cả các Performance API khác được thảo luận trên trang này. Việc hiểu rõ chỉ số này là rất quan trọng để thu thập dữ liệu chất lượng.
Bạn có thể dùng PerformanceObserver để thụ động đăng ký các sự kiện liên quan đến hiệu suất. Điều này cho phép các lệnh gọi lại API kích hoạt trong khoảng thời gian rảnh, tức là các lệnh gọi lại này thường sẽ không ảnh hưởng đến hiệu suất của trang.
Để tạo một PerformanceObserver, hãy truyền cho nó một lệnh gọi lại để chạy bất cứ khi nào các mục nhập hiệu suất mới được gửi đi. Sau đó, bạn cho trình theo dõi biết những loại mục cần theo dõi bằng phương thức 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'});
Các phần sau đây liệt kê tất cả các loại mục nhập có sẵn để quan sát, nhưng trong các trình duyệt mới hơn, bạn có thể kiểm tra những loại mục nhập có sẵn thông qua thuộc tính tĩnh PerformanceObserver.supportedEntryTypes.
Quan sát những mục đã xảy ra
Theo mặc định, các đối tượng PerformanceObserver chỉ có thể quan sát các mục khi chúng xuất hiện. Điều này có thể gây ra vấn đề nếu bạn muốn tải mã phân tích hiệu suất một cách trì hoãn để mã này không chặn các tài nguyên có mức độ ưu tiên cao hơn.
Để nhận các mục nhập trước đây (sau khi chúng xảy ra), hãy đặt cờ buffered thành true khi bạn gọi observe(). Trình duyệt sẽ bao gồm các mục trong nhật ký từ vùng đệm mục hiệu suất vào lần đầu tiên lệnh gọi lại PerformanceObserver của bạn được gọi, tối đa là kích thước vùng đệm tối đa cho loại đó.
po.observe({
type: 'some-entry-type',
buffered: true,
});
Các API hiệu suất cũ cần tránh
Trước Performance Observer API, nhà phát triển có thể truy cập vào các mục nhập hiệu suất bằng 3 phương thức sau đây được xác định trên đối tượng performance:
Mặc dù các API này vẫn được hỗ trợ, nhưng bạn không nên sử dụng vì chúng không cho phép bạn theo dõi thời điểm các mục mới được phát. Ngoài ra, nhiều API mới (chẳng hạn như largest-contentful-paint) không được hiển thị thông qua đối tượng performance, mà chỉ được hiển thị thông qua PerformanceObserver.
Trừ phi bạn cần có khả năng tương thích với Internet Explorer, tốt nhất là bạn nên tránh dùng các phương thức này trong mã và sử dụng PerformanceObserver từ nay về sau.
User Timing API
User Timing API là một API đo lường đa năng cho các chỉ số dựa trên thời gian. API này cho phép bạn đánh dấu tuỳ ý các điểm theo thời gian, sau đó đo khoảng thời gian giữa các dấu đó.
// 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');
Mặc dù các API như Date.now() hoặc performance.now() mang lại cho bạn những khả năng tương tự, nhưng lợi ích của việc sử dụng User Timing API là API này tích hợp tốt với công cụ hiệu suất. Ví dụ: Chrome DevTools trực quan hoá các phép đo Thời gian của người dùng trong bảng điều khiển Hiệu suất và nhiều nhà cung cấp dịch vụ phân tích cũng sẽ tự động theo dõi mọi phép đo mà bạn thực hiện và gửi dữ liệu về thời lượng đến phần phụ trợ phân tích của họ.
Để báo cáo các phép đo Thời gian của người dùng, bạn có thể sử dụng PerformanceObserver và đăng ký để theo dõi các mục nhập thuộc loại 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 rất hữu ích khi bạn muốn biết thời điểm luồng chính của trình duyệt bị chặn đủ lâu để ảnh hưởng đến tốc độ khung hình hoặc độ trễ đầu vào. API sẽ báo cáo mọi tác vụ thực thi lâu hơn 50 mili giây.
Bất cứ khi nào bạn cần chạy mã tốn nhiều tài nguyên hoặc tải và thực thi các tập lệnh lớn, bạn nên theo dõi xem mã đó có chặn luồng chính hay không. Trên thực tế, nhiều chỉ số cấp cao được xây dựng dựa trên chính Long Tasks API (chẳng hạn như Thời gian tương tác (TTI) và Tổng thời gian chặn (TBT)).
Để xác định thời điểm các tác vụ dài xảy ra, bạn có thể sử dụng PerformanceObserver và đăng ký để theo dõi các mục nhập thuộc loại 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 là một phiên bản mới của Long Tasks API, xem xét các khung hình dài (thay vì các tác vụ dài) có thời lượng trên 50 mili giây. Điều này giải quyết một số thiếu sót của Long Tasks API, bao gồm cả khả năng phân bổ tốt hơn và phạm vi rộng hơn của các độ trễ có thể gây ra vấn đề.
Để xác định thời điểm xảy ra khung hình dài, bạn có thể sử dụng PerformanceObserver và đăng ký để theo dõi các mục nhập thuộc loại 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
Chỉ số Nội dung lớn nhất hiển thị (LCP) rất hữu ích khi bạn muốn biết thời điểm hình ảnh hoặc khối văn bản lớn nhất được hiển thị trên màn hình, nhưng trong một số trường hợp, bạn muốn đo thời gian kết xuất của một phần tử khác.
Đối với những trường hợp này, hãy sử dụng Element Timing API. LCP API thực sự được xây dựng dựa trên Element Timing API và bổ sung tính năng báo cáo tự động về phần tử nội dung lớn nhất, nhưng bạn cũng có thể báo cáo về các phần tử khác bằng cách thêm thuộc tính elementtiming một cách rõ ràng vào các phần tử đó và đăng ký PerformanceObserver để theo dõi loại mục nhập 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
Chỉ số Lượt tương tác đến nội dung hiển thị tiếp theo (INP) đánh giá khả năng phản hồi tổng thể của trang bằng cách quan sát tất cả hoạt động tương tác bằng cách nhấp, nhấn và bàn phím trong suốt thời gian tồn tại của một trang. INP của một trang thường là lượt tương tác mất nhiều thời gian nhất để hoàn tất, từ thời điểm người dùng bắt đầu tương tác cho đến thời điểm trình duyệt hiển thị khung hình tiếp theo cho thấy kết quả trực quan của dữ liệu đầu vào của người dùng.
Chỉ số INP có được là nhờ Event Timing API. API này hiển thị một số dấu thời gian xảy ra trong vòng đời của sự kiện, bao gồm:
startTime: thời gian trình duyệt nhận được sự kiện.processingStart: thời gian mà trình duyệt có thể bắt đầu xử lý trình xử lý sự kiện cho sự kiện.processingEnd: thời gian mà trình duyệt hoàn tất việc thực thi tất cả mã đồng bộ được bắt đầu từ trình xử lý sự kiện cho sự kiện này.duration: thời gian (làm tròn đến 8 mili giây vì lý do bảo mật) từ khi trình duyệt nhận được sự kiện cho đến khi có thể vẽ khung hình tiếp theo sau khi hoàn tất việc thực thi tất cả mã đồng bộ được khởi tạo từ trình xử lý sự kiện.
Ví dụ sau đây cho thấy cách sử dụng các giá trị này để tạo chỉ số tuỳ chỉnh:
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 cung cấp cho nhà phát triển thông tin chi tiết về cách tải tài nguyên cho một trang cụ thể. Mặc dù có tên là API, nhưng thông tin mà API này cung cấp không chỉ giới hạn ở dữ liệu về thời gian (mặc dù có rất nhiều dữ liệu đó). Những dữ liệu khác mà bạn có thể truy cập bao gồm:
initiatorType: cách tìm nạp tài nguyên: chẳng hạn như từ thẻ<script>hoặc<link>, hoặc từ lệnh gọifetch().nextHopProtocol: giao thức dùng để tìm nạp tài nguyên, chẳng hạn nhưh2hoặcquic.encodedBodySize/decodedBodySize]: kích thước của tài nguyên ở dạng được mã hoá hoặc giải mã (tương ứng)transferSize: kích thước của tài nguyên thực sự được truyền qua mạng. Khi bộ nhớ đệm đáp ứng các tài nguyên, giá trị này có thể nhỏ hơn nhiều so vớiencodedBodySizevà trong một số trường hợp, giá trị này có thể bằng 0 (nếu không cần xác thực lại bộ nhớ đệm).
Bạn có thể sử dụng thuộc tính transferSize của các mục thời gian tài nguyên để đo lường chỉ số tỷ lệ truy cập bộ nhớ đệm hoặc chỉ số tổng kích thước tài nguyên được lưu vào bộ nhớ đệm. Điều này có thể hữu ích trong việc tìm hiểu cách chiến lược lưu tài nguyên vào bộ nhớ đệm ảnh hưởng đến hiệu suất của khách truy cập nhiều lần.
Ví dụ sau đây ghi lại tất cả tài nguyên mà trang yêu cầu và cho biết liệu bộ nhớ đệm có đáp ứng từng tài nguyên hay không.
// 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 tương tự như Resource Timing API, nhưng chỉ báo cáo yêu cầu điều hướng. Loại mục nhập navigation cũng tương tự như loại mục nhập resource, nhưng chứa một số thông tin bổ sung chỉ dành riêng cho các yêu cầu điều hướng (chẳng hạn như khi các sự kiện DOMContentLoaded và load kích hoạt).
Một chỉ số mà nhiều nhà phát triển theo dõi để nắm được thời gian phản hồi của máy chủ (Thời gian cho byte đầu tiên (TTFB)) có sẵn khi sử dụng Navigation Timing API (cụ thể là dấu thời gian responseStart của mục nhập).
// 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});
Một chỉ số khác mà nhà phát triển sử dụng trình chạy dịch vụ có thể quan tâm là thời gian khởi động trình chạy dịch vụ cho các yêu cầu điều hướng. Đây là khoảng thời gian cần thiết để trình duyệt bắt đầu luồng trình chạy dịch vụ trước khi có thể bắt đầu chặn các sự kiện tìm nạp.
Bạn có thể xác định thời gian khởi động của worker dịch vụ cho một yêu cầu điều hướng cụ thể dựa trên mức chênh lệch giữa entry.responseStart và 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 cho phép bạn truyền dữ liệu thời gian cụ thể theo yêu cầu từ máy chủ đến trình duyệt thông qua tiêu đề phản hồi. Ví dụ: bạn có thể cho biết thời gian cần thiết để tra cứu dữ liệu trong cơ sở dữ liệu cho một yêu cầu cụ thể. Điều này có thể hữu ích trong việc gỡ lỗi các vấn đề về hiệu suất do máy chủ hoạt động chậm.
Đối với những nhà phát triển sử dụng nhà cung cấp dịch vụ phân tích bên thứ ba, Server Timing API là cách duy nhất để tương quan dữ liệu hiệu suất máy chủ với các chỉ số kinh doanh khác mà những công cụ phân tích này có thể đo lường.
Để chỉ định dữ liệu về thời gian của máy chủ trong các phản hồi, bạn có thể sử dụng tiêu đề phản hồi Server-Timing. Dưới đây là một ví dụ.
HTTP/1.1 200 OK
Server-Timing: miss, db;dur=53, app;dur=47.2
Sau đó, từ các trang của mình, bạn có thể đọc dữ liệu này trên cả mục resource hoặc navigation trong API Thời gian tài nguyên và API Thời gian điều hướng.
// 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});