Tìm hiểu sâu về Web Performance API, từ các phép đo thời gian truyền thống đến các chỉ số lấy người dùng làm trung tâm hiện đại như Core Web Vitals và cách kết nối chúng để có cái nhìn toàn diện về hiệu suất.
Vượt ra ngoài Đồng hồ: Kết nối Web Performance API với Trải nghiệm Người dùng Thực tế
Trong nền kinh tế kỹ thuật số, tốc độ không chỉ là một tính năng; nó là nền tảng của trải nghiệm người dùng. Một trang web chậm có thể dẫn đến người dùng khó chịu, tỷ lệ thoát cao hơn và tác động trực tiếp đến doanh thu. Trong nhiều năm, các nhà phát triển đã dựa vào các số liệu thời gian như window.onload
để đánh giá hiệu suất. Nhưng liệu thời gian tải nhanh có thực sự tương đương với một người dùng hài lòng? Câu trả lời thường là không.
Một trang có thể hoàn tất việc tải tất cả các tài nguyên kỹ thuật của nó trong vòng chưa đầy một giây, nhưng lại cảm thấy chậm chạp và không thể sử dụng được đối với một người thực đang cố gắng tương tác với nó. Sự ngắt kết nối này làm nổi bật một sự phát triển quan trọng trong phát triển web: sự chuyển đổi từ việc đo lường thời gian kỹ thuật sang định lượng trải nghiệm của con người. Hiệu suất web hiện đại là một câu chuyện về hai quan điểm: dữ liệu chi tiết, cấp thấp được cung cấp bởi Web Performance API và các số liệu cấp cao, lấy người dùng làm trung tâm như Core Web Vitals của Google.
Hướng dẫn toàn diện này sẽ thu hẹp khoảng cách đó. Chúng ta sẽ khám phá bộ Web Performance API mạnh mẽ đóng vai trò là công cụ chẩn đoán của chúng ta. Sau đó, chúng ta sẽ đi sâu vào các số liệu trải nghiệm người dùng hiện đại cho chúng ta biết hiệu suất *cảm thấy* như thế nào. Quan trọng nhất, chúng ta sẽ kết nối các điểm, chỉ cho bạn cách sử dụng dữ liệu thời gian cấp thấp để chẩn đoán và khắc phục các nguyên nhân gốc rễ của trải nghiệm người dùng kém cho khán giả toàn cầu của bạn.
Nền tảng: Hiểu Web Performance API
Web Performance API là một tập hợp các giao diện trình duyệt được tiêu chuẩn hóa, cho phép các nhà phát triển truy cập vào dữ liệu thời gian rất chi tiết và chính xác liên quan đến việc điều hướng và hiển thị một trang web. Chúng là nền tảng của việc đo lường hiệu suất, cho phép chúng ta vượt ra ngoài đồng hồ bấm giờ đơn giản và hiểu được điệu nhảy phức tạp của các yêu cầu mạng, phân tích cú pháp và hiển thị.
Navigation Timing API: Hành trình của Trang
Navigation Timing API cung cấp một phân tích chi tiết về thời gian cần thiết để tải tài liệu chính. Nó ghi lại các cột mốc quan trọng từ thời điểm người dùng bắt đầu điều hướng (như nhấp vào một liên kết) đến thời điểm trang được tải đầy đủ. Đây là cái nhìn đầu tiên và cơ bản nhất của chúng ta về quá trình tải trang.
Bạn có thể truy cập dữ liệu này bằng một lệnh gọi JavaScript đơn giản:
const navigationEntry = performance.getEntriesByType('navigation')[0];
console.log(navigationEntry.toJSON());
Điều này trả về một đối tượng chứa đầy dấu thời gian. Một số thuộc tính chính bao gồm:
- fetchStart: Khi trình duyệt bắt đầu tìm nạp tài liệu.
- responseStart: Khi trình duyệt nhận được byte đầu tiên của phản hồi từ máy chủ. Thời gian giữa
fetchStart
vàresponseStart
thường được gọi là Time to First Byte (TTFB). - domContentLoadedEventEnd: Khi tài liệu HTML ban đầu đã được tải và phân tích cú pháp hoàn toàn, mà không cần chờ kiểu dáng, hình ảnh và khung con tải xong.
- loadEventEnd: Khi tất cả các tài nguyên cho trang (bao gồm hình ảnh, CSS, v.v.) đã được tải đầy đủ.
Trong một thời gian dài, loadEventEnd
là tiêu chuẩn vàng. Tuy nhiên, hạn chế của nó là rất nghiêm trọng: nó không nói gì về thời điểm người dùng *nhìn thấy* nội dung có ý nghĩa hoặc khi nào họ có thể *tương tác* với trang. Đó là một cột mốc kỹ thuật, không phải là một cột mốc của con người.
Resource Timing API: Phân tích các Thành phần
Một trang web hiếm khi là một tệp duy nhất. Nó là một tập hợp của HTML, CSS, JavaScript, hình ảnh, phông chữ và các lệnh gọi API. Resource Timing API cho phép bạn kiểm tra thời gian mạng cho từng tài nguyên riêng lẻ này.
Điều này cực kỳ mạnh mẽ để xác định các tắc nghẽn. Một hình ảnh anh hùng lớn, chưa được tối ưu hóa từ Mạng phân phối nội dung (CDN) ở một lục địa khác có làm chậm quá trình hiển thị ban đầu không? Một tập lệnh phân tích của bên thứ ba có chặn luồng chính không? Resource Timing giúp bạn trả lời những câu hỏi này.
Bạn có thể lấy danh sách tất cả các tài nguyên như sau:
const resourceEntries = performance.getEntriesByType('resource');
resourceEntries.forEach(resource => {
if (resource.duration > 200) { // Tìm tài nguyên mất nhiều thời gian hơn 200ms
console.log(`Tài nguyên chậm: ${resource.name}, Thời lượng: ${resource.duration}ms`);
}
});
Các thuộc tính chính bao gồm name
(URL của tài nguyên), initiatorType
(nguyên nhân khiến tài nguyên được tải, ví dụ: 'img', 'script') và duration
(tổng thời gian cần thiết để tìm nạp nó).
User Timing API: Đo lường Logic của Ứng dụng của Bạn
Đôi khi, tắc nghẽn hiệu suất không nằm ở việc tải tài sản mà ở chính mã phía máy khách. Mất bao lâu để ứng dụng một trang (SPA) của bạn hiển thị một thành phần phức tạp sau khi dữ liệu được nhận từ một API? User Timing API cho phép bạn tạo các phép đo tùy chỉnh, dành riêng cho ứng dụng.
Nó hoạt động với hai phương pháp chính:
- performance.mark(name): Tạo một dấu thời gian được đặt tên trong bộ đệm hiệu suất.
- performance.measure(name, startMark, endMark): Tính toán thời lượng giữa hai dấu và tạo một phép đo được đặt tên.
Ví dụ: Đo thời gian hiển thị của một thành phần danh sách sản phẩm.
// Khi bạn bắt đầu tìm nạp dữ liệu
performance.mark('product-list-fetch-start');
fetch('/api/products')
.then(response => response.json())
.then(data => {
// Sau khi tìm nạp, trước khi hiển thị
performance.mark('product-list-render-start');
renderProductList(data);
// Ngay sau khi quá trình hiển thị hoàn tất
performance.mark('product-list-render-end');
// Tạo một phép đo
performance.measure(
'Product List Render Time',
'product-list-render-start',
'product-list-render-end'
);
});
Điều này cho phép bạn kiểm soát chính xác để đo lường các phần của ứng dụng của bạn quan trọng nhất đối với quy trình làm việc của người dùng.
PerformanceObserver: Phương pháp Tiếp cận Hiện đại, Hiệu quả
Liên tục thăm dò performance.getEntriesByType()
là không hiệu quả. PerformanceObserver API cung cấp một cách tốt hơn nhiều để lắng nghe các mục hiệu suất. Bạn đăng ký các loại mục cụ thể và trình duyệt sẽ thông báo cho hàm gọi lại của bạn một cách không đồng bộ khi chúng được ghi lại. Đây là cách được khuyến nghị để thu thập dữ liệu hiệu suất mà không làm tăng thêm chi phí cho ứng dụng của bạn.
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(`Loại mục: ${entry.entryType}, Tên: ${entry.name}`);
}
});
observer.observe({ entryTypes: ['resource', 'navigation', 'mark', 'measure'] });
Trình quan sát này là chìa khóa để thu thập không chỉ các số liệu truyền thống ở trên mà còn cả các số liệu hiện đại, lấy người dùng làm trung tâm mà chúng ta sẽ thảo luận tiếp theo.
Sự chuyển đổi sang Lấy người dùng làm trung tâm: Core Web Vitals
Biết rằng một trang được tải trong 2 giây là hữu ích, nhưng nó không trả lời các câu hỏi quan trọng: Người dùng có nhìn chằm chằm vào một màn hình trống trong 2 giây đó không? Họ có thể tương tác với trang hay nó bị đóng băng? Nội dung có nhảy xung quanh một cách bất ngờ khi họ cố gắng đọc không?
Để giải quyết vấn đề này, Google đã giới thiệu Core Web Vitals (CWV), một tập hợp các số liệu được thiết kế để đo lường trải nghiệm người dùng thực tế của một trang trên ba khía cạnh chính: tải, tính tương tác và tính ổn định trực quan.
Largest Contentful Paint (LCP): Đo lường Tải nhận thức
LCP đo thời gian hiển thị của hình ảnh hoặc khối văn bản lớn nhất hiển thị trong khung nhìn. Nó là một proxy tuyệt vời cho thời điểm người dùng cảm thấy nội dung chính của trang đã được tải. Nó trả lời trực tiếp câu hỏi của người dùng: "Trang này đã hữu ích chưa?"
- Tốt: Dưới 2,5 giây
- Cần cải thiện: Giữa 2,5 giây và 4,0 giây
- Kém: Trên 4,0 giây
Không giống như loadEventEnd
, LCP tập trung vào những gì người dùng nhìn thấy đầu tiên, làm cho nó phản ánh chính xác hơn nhiều về tốc độ tải nhận thức.
Interaction to Next Paint (INP): Đo lường Khả năng phản hồi
INP là người kế thừa của First Input Delay (FID) và trở thành một Core Web Vital chính thức vào tháng 3 năm 2024. Trong khi FID chỉ đo độ trễ của tương tác *đầu tiên*, INP đo độ trễ của *tất cả* các tương tác của người dùng (nhấp chuột, chạm, nhấn phím) trong suốt vòng đời của trang. Nó báo cáo tương tác dài nhất, xác định hiệu quả khả năng phản hồi trường hợp xấu nhất mà người dùng trải nghiệm.
INP đo toàn bộ thời gian từ đầu vào của người dùng cho đến khi khung hình tiếp theo được vẽ, phản ánh phản hồi trực quan. Nó trả lời câu hỏi của người dùng: "Khi tôi nhấp vào nút này, trang có phản hồi nhanh chóng không?"
- Tốt: Dưới 200 mili giây
- Cần cải thiện: Giữa 200ms và 500ms
- Kém: Trên 500ms
INP cao thường do luồng chính bận, nơi các tác vụ JavaScript chạy dài ngăn trình duyệt phản hồi đầu vào của người dùng.
Cumulative Layout Shift (CLS): Đo lường Tính ổn định Trực quan
CLS đo lường sự ổn định trực quan của một trang. Nó định lượng mức độ nội dung di chuyển xung quanh một cách bất ngờ trên màn hình trong quá trình tải. Điểm CLS cao là một nguồn gây khó chịu phổ biến cho người dùng, chẳng hạn như khi bạn cố gắng nhấp vào một nút, nhưng một quảng cáo tải phía trên nó, đẩy nút xuống và khiến bạn nhấp vào quảng cáo thay thế.
CLS trả lời câu hỏi của người dùng: "Tôi có thể sử dụng trang này mà không cần các thành phần nhảy khắp nơi không?"
- Tốt: Dưới 0,1
- Cần cải thiện: Giữa 0,1 và 0,25
- Kém: Trên 0,25
Các nguyên nhân phổ biến của CLS cao bao gồm hình ảnh hoặc iframe không có kích thước, phông chữ web tải muộn hoặc nội dung được chèn động vào trang mà không dành không gian cho nó.
Thu hẹp Khoảng cách: Sử dụng API để Chẩn đoán Trải nghiệm Người dùng Kém
Đây là nơi mọi thứ kết hợp với nhau. Core Web Vitals cho chúng ta biết *những gì* người dùng đã trải nghiệm (ví dụ: LCP chậm). Web Performance API cho chúng ta biết *tại sao* nó xảy ra. Bằng cách kết hợp chúng, chúng ta chuyển đổi từ chỉ đơn giản là quan sát hiệu suất sang chủ động chẩn đoán và khắc phục nó.
Chẩn đoán LCP Chậm
Hãy tưởng tượng công cụ Giám sát Người dùng Thực (RUM) của bạn báo cáo LCP kém là 4,5 giây cho người dùng ở một khu vực cụ thể. Làm thế nào để bạn sửa nó? Bạn cần chia nhỏ thời gian LCP thành các phần cấu thành của nó.
- Time to First Byte (TTFB): Máy chủ có phản hồi chậm không? Sử dụng Navigation Timing API. Thời lượng
responseStart - requestStart
cho bạn một TTFB chính xác. Nếu điều này cao, vấn đề nằm ở phần phụ trợ, cấu hình máy chủ hoặc cơ sở dữ liệu của bạn, chứ không phải là phần giao diện người dùng. - Resource Load Delay & Time: Bản thân thành phần LCP có tải chậm không? Đầu tiên, xác định thành phần LCP (ví dụ: hình ảnh anh hùng). Bạn có thể sử dụng
PerformanceObserver
cho'largest-contentful-paint'
để lấy chính thành phần đó. Sau đó, sử dụng Resource Timing API để tìm mục nhập cho URL của thành phần đó. Phân tích dòng thời gian của nó: CóconnectStart
đếnconnectEnd
dài không (mạng chậm)?responseStart
đếnresponseEnd
có dài không (kích thước tệp lớn)?fetchStart
của nó có bị trì hoãn vì nó bị chặn bởi các tài nguyên chặn hiển thị khác như CSS hoặc JavaScript không? - Element Render Delay: Đây là thời gian sau khi tài nguyên hoàn tất việc tải cho đến khi nó thực sự được vẽ trên màn hình. Điều này có thể là do luồng chính đang bận với các tác vụ khác, chẳng hạn như thực thi một gói JavaScript lớn.
Bằng cách sử dụng Navigation và Resource Timing, bạn có thể xác định xem LCP chậm là do máy chủ chậm, tập lệnh chặn hiển thị hay hình ảnh lớn, chưa được tối ưu hóa.
Điều tra INP Kém
Người dùng của bạn đang phàn nàn rằng việc nhấp vào nút "Thêm vào giỏ hàng" có cảm giác bị lag. Số liệu INP của bạn nằm trong phạm vi "Kém". Đây hầu như luôn là một vấn đề về luồng chính.
- Xác định Tác vụ Dài: Long Tasks API là công cụ chính của bạn ở đây. Nó báo cáo bất kỳ tác vụ nào trên luồng chính mất hơn 50ms, vì bất kỳ điều gì dài hơn đều có nguy cơ gây ra sự chậm trễ đáng chú ý cho người dùng. Thiết lập
PerformanceObserver
để lắng nghe các mục'longtask'
. - Tương quan với Hành động của Người dùng: Một tác vụ dài chỉ là một vấn đề nếu nó xảy ra khi người dùng đang cố gắng tương tác. Bạn có thể tương quan
startTime
của một sự kiện INP (được quan sát thông quaPerformanceObserver
trên loại'event'
) với thời gian của bất kỳ tác vụ dài nào xảy ra cùng thời điểm. Điều này cho bạn biết chính xác hàm JavaScript nào đã chặn tương tác của người dùng. - Đo lường Trình xử lý Cụ thể: Sử dụng User Timing API để có được độ chi tiết cao hơn nữa. Bọc các trình xử lý sự kiện quan trọng của bạn (như trình xử lý 'click' cho "Thêm vào giỏ hàng") bằng
performance.mark()
vàperformance.measure()
. Điều này sẽ cho bạn biết chính xác mã của bạn mất bao lâu để thực thi và liệu nó có phải là nguồn gốc của tác vụ dài hay không.
Giải quyết CLS Cao
Người dùng báo cáo rằng văn bản nhảy xung quanh khi họ đang đọc một bài viết trên thiết bị di động của họ. Điểm CLS của bạn là 0,3.
- Quan sát Sự thay đổi Bố cục: Sử dụng
PerformanceObserver
để lắng nghe các mục'layout-shift'
. Mỗi mục nhập sẽ có mộtvalue
(đóng góp của nó vào điểm CLS) và một danh sáchsources
, là các thành phần DOM đã di chuyển. Điều này cho bạn biết *những gì* đã di chuyển. - Tìm Tài nguyên Thủ phạm: Câu hỏi tiếp theo là *tại sao* nó di chuyển. Một lý do phổ biến là một tài nguyên tải muộn và đẩy nội dung khác xuống. Bạn có thể tương quan
startTime
của một mụclayout-shift
với thời gianresponseEnd
của các mục từ Resource Timing API. Nếu một sự thay đổi bố cục xảy ra ngay sau khi một tập lệnh quảng cáo hoặc một hình ảnh lớn hoàn tất việc tải, bạn có thể đã tìm thấy thủ phạm của mình. - Giải pháp Chủ động: Việc sửa chữa thường liên quan đến việc cung cấp kích thước cho hình ảnh và quảng cáo (
) hoặc dành không gian trên trang cho nội dung động trước khi nó tải. Resource Timing giúp bạn xác định tài nguyên nào bạn cần chủ động về nó.
Triển khai Thực tế: Xây dựng một Hệ thống Giám sát Toàn cầu
Hiểu các API này là một chuyện; triển khai chúng để giám sát trải nghiệm của cơ sở người dùng toàn cầu của bạn là bước tiếp theo. Đây là lĩnh vực của Giám sát Người dùng Thực (RUM).
Kết hợp Tất cả với PerformanceObserver
Bạn có thể tạo một tập lệnh duy nhất, mạnh mẽ để thu thập tất cả dữ liệu quan trọng này. Mục tiêu là thu thập các số liệu và ngữ cảnh của chúng mà không ảnh hưởng đến hiệu suất mà bạn đang cố gắng đo lường.
Đây là một đoạn mã khái niệm về thiết lập trình quan sát mạnh mẽ:
const collectedMetrics = {};
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'largest-contentful-paint') {
collectedMetrics.lcp = entry.startTime;
} else if (entry.entryType === 'layout-shift') {
collectedMetrics.cls = (collectedMetrics.cls || 0) + entry.value;
} else if (entry.entryType === 'event') {
// Đây là một cái nhìn đơn giản hóa về tính toán INP
const duration = entry.duration;
if (duration > (collectedMetrics.inp || 0)) {
collectedMetrics.inp = duration;
}
}
// ... và vân vân cho các loại mục khác như 'longtask'
}
});
observer.observe({ entryTypes: ['largest-contentful-paint', 'layout-shift', 'event', 'longtask'] });
Gửi Dữ liệu một cách Đáng tin cậy
Sau khi bạn đã thu thập dữ liệu của mình, bạn cần gửi nó đến một phần phụ trợ phân tích để lưu trữ và phân tích. Điều quan trọng là phải làm điều này mà không làm chậm quá trình dỡ trang hoặc mất dữ liệu từ những người dùng đóng các tab của họ một cách nhanh chóng.
API navigator.sendBeacon()
là hoàn hảo cho việc này. Nó cung cấp một cách không đồng bộ, đáng tin cậy để gửi một lượng nhỏ dữ liệu đến máy chủ, ngay cả khi trang đang dỡ tải. Nó không mong đợi phản hồi, làm cho nó nhẹ và không chặn.
window.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
const payload = JSON.stringify(collectedMetrics);
navigator.sendBeacon('/api/performance-analytics', payload);
}
});
Tầm quan trọng của một Cái nhìn Toàn cầu
Các công cụ kiểm tra trong phòng thí nghiệm như Lighthouse là vô giá, nhưng chúng chạy trong một môi trường được kiểm soát. Dữ liệu RUM được thu thập từ các API này cho bạn biết sự thật cơ bản về những gì người dùng của bạn trải nghiệm trên các quốc gia, điều kiện mạng và thiết bị khác nhau.
Khi phân tích dữ liệu của bạn, hãy luôn phân đoạn nó. Bạn có thể khám phá ra rằng:
- LCP của bạn là tuyệt vời cho người dùng ở Bắc Mỹ nhưng kém cho người dùng ở Úc vì máy chủ hình ảnh chính của bạn được đặt tại Hoa Kỳ.
- INP của bạn cao trên các thiết bị Android tầm trung, phổ biến ở các thị trường mới nổi, vì JavaScript của bạn quá tốn CPU đối với chúng.
- CLS của bạn chỉ là một vấn đề trên các kích thước màn hình cụ thể, nơi một truy vấn phương tiện CSS gây ra sự thay đổi kích thước không đúng cách của quảng cáo.
Mức độ hiểu biết được phân đoạn này cho phép bạn ưu tiên các tối ưu hóa sẽ có tác động đáng kể nhất đến cơ sở người dùng thực tế của bạn, bất kể họ ở đâu.
Kết luận: Từ Đo lường đến Làm chủ
Thế giới hiệu suất web đã trưởng thành. Chúng ta đã chuyển từ thời gian kỹ thuật đơn giản sang hiểu biết tinh vi về trải nghiệm nhận thức của người dùng. Cuộc hành trình bao gồm ba bước chính:
- Đo lường Trải nghiệm: Sử dụng
PerformanceObserver
để thu thập Core Web Vitals (LCP, INP, CLS). Điều này cho bạn biết *điều gì* đang xảy ra và *nó cảm thấy như thế nào* đối với người dùng. - Chẩn đoán Nguyên nhân: Sử dụng các API Thời gian cơ bản (Navigation, Resource, User, Long Tasks) để đào sâu hơn. Điều này cho bạn biết *tại sao* trải nghiệm lại kém.
- Hành động với Độ chính xác: Sử dụng dữ liệu kết hợp để đưa ra các tối ưu hóa có mục tiêu, có thông tin, giải quyết nguyên nhân gốc rễ của vấn đề cho các phân khúc người dùng cụ thể.
Bằng cách làm chủ cả các số liệu người dùng cấp cao và các API chẩn đoán cấp thấp, bạn có thể xây dựng một chiến lược hiệu suất toàn diện. Bạn ngừng đoán và bắt đầu thiết kế một trải nghiệm web không chỉ nhanh về mặt kỹ thuật mà còn cảm thấy nhanh, nhạy bén và thú vị đối với mọi người dùng, trên mọi thiết bị, ở mọi nơi trên thế giới.