Việc xác định xem một trang có được tải ban đầu ở chế độ nền hay không (ví dụ: mở trong một thẻ nền mới) là rất quan trọng để theo dõi hiệu suất chính xác. Các trang được tải ở chế độ nền thường có thời gian hiển thị bị trì hoãn và thời gian đo lường dài hơn (chẳng hạn như Nội dung đầu tiên hiển thị). Việc xác định các trang này giúp bạn lọc chúng ra khỏi số liệu phân tích hiệu suất để tránh dữ liệu bị sai lệch.
Cách chính xác nhất để đo lường chỉ số này là sử dụng API VisibilityStateEntry. API này ghi lại một cách đáng tin cậy các thay đổi về chế độ hiển thị trên dòng thời gian hiệu suất của trình duyệt, bất kể thời điểm tập lệnh của bạn thực sự thực thi.
Phát hiện chế độ hiển thị ban đầu và thời gian chạy trong nền
BẮT BUỘC: Sử dụng performance.getEntriesByType('visibility-state') để truy cập vào nhật ký chính xác về chế độ hiển thị. Đừng chỉ dựa vào việc kiểm tra document.visibilityState tại thời điểm thực thi, vì việc này dễ dẫn đến tình huống tương tranh.
/**
* Accurately determines visibility state history using the Performance API.
*/
function getVisibilityInfo() {
// Retrieve VisibilityStateEntry members:
const entries = performance.getEntriesByType('visibility-state');
if (entries.length > 0) {
const firstEntry = entries[0];
// If the first performance entry for visibility is 'hidden',
// the page was loaded in the background.
const initiallyBackgrounded = firstEntry.name === 'hidden';
// Find the precise, high-resolution timestamp of when the page
// was first backgrounded.
let timeBackgrounded = null;
for (const entry of entries) {
if (entry.name === 'hidden') {
// entry.startTime is used because it provides the exact browser
// timestamp of the visibility change, which is required for precision
timeBackgrounded = entry.startTime;
break;
}
}
return {
initiallyBackgrounded,
timeBackgrounded
};
}
}
Chiến lược dự phòng
Đối với các môi trường không được hỗ trợ, bạn có thể quay lại kiểm tra thuộc tính document.visibilityState hoặc theo dõi sự kiện visibilitychange.
BẮT BUỘC: Bạn phải hiểu rằng phương pháp dự phòng này thường rất không chính xác khi xác định trạng thái nền ban đầu. Vì tập lệnh có thể tải và thực thi không đồng bộ, nên người dùng có thể mở một trang trong thẻ nền rồi đưa trang đó lên nền trước trước khi tập lệnh của bạn tải xuống và thực thi xong. Khi tập lệnh của bạn cuối cùng cũng chạy, document.visibilityState sẽ đọc đồng bộ dưới dạng 'visible' và bạn sẽ giả định không chính xác rằng trang đã được tải ở nền trước, hoàn toàn bỏ lỡ trạng thái ẩn ban đầu của trang. Hơn nữa, dấu thời gian dự phòng thiếu độ chính xác nội bộ của Performance API. Nếu độ chính xác là ưu tiên hàng đầu, đừng sử dụng phương án dự phòng.
/**
* Fallback implementation using document.visibilityState.
* This approach is prone to race conditions if the script loads asynchronously.
*/
function getFallbackVisibilityInfo() {
// Check the state exactly when this script executes.
// This will fail to detect an initial background state if the user
// foregrounded the page before this script executed.
let initiallyBackgrounded = document.visibilityState === 'hidden';
// If it's hidden now, we approximate that it was hidden from load (time 0).
let timeBackgrounded = initiallyBackgrounded ? 0 : null;
// Listen for future visibility changes to capture if it is backgrounded later.
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden' && timeBackgrounded === null) {
// performance.now() is used here as a fallback, but it only gives
// us the time the event listener fired, not the precise internal
// browser time the visibility actually changed.
timeBackgrounded = performance.now();
}
});
return {
get initiallyBackgrounded() { return initiallyBackgrounded; },
get timeBackgrounded() { return timeBackgrounded; }
};
}
// Modern implementation using VisibilityStateEntry API.
function getVisibilityInfo() {
// Code omitted here would be the same modern
// implementation shown earlier in this guide
// ...
}
// DO: Detect if the VisibilityStateEntry API is available
if ('VisibilityStateEntry' in window) {
// DO: If VisibilityStateEntry is available, use it first:
getVisibilityInfo();
} else {
// DO: If VisibilityStateEntry is unavailable, fall back to `document.visibilityState`:
getFallbackVisibilityInfo();
}