如要準確監控效能,就必須判斷網頁是否一開始在背景載入 (例如在新的背景分頁中開啟)。在背景載入的網頁通常會延遲轉譯,且指標時間較長 (例如 First Contentful Paint)。找出這些網頁後,即可將其從成效分析中篩除,避免資料失真。
最準確的評估方式是使用 VisibilityStateEntry API,無論指令碼實際執行的時間為何,這個 API 都能可靠地記錄瀏覽器效能時間軸上的瀏覽權限變更。
偵測初始顯示和背景時間
強制:請使用 performance.getEntriesByType('visibility-state') 存取確切的曝光度記錄。請勿只在執行時檢查 document.visibilityState,因為這容易發生競爭狀況。
/**
* 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
};
}
}
備用策略
在不支援的環境中,您可以改為檢查 document.visibilityState 屬性或監聽 visibilitychange 事件。
強制:請務必瞭解,這種備用方法在判斷初始背景狀態時往往非常不準確。由於指令碼可以非同步載入及執行,因此網頁可能會在背景分頁中開啟,然後在指令碼完成下載和執行「之前」,由使用者移至前景。當指令碼最終執行時,document.visibilityState 會同步讀取為 'visible',而您會誤以為頁面是在前景載入,完全錯過其初始隱藏狀態。此外,備用時間戳記缺乏 Performance API 的內部精確度。如果精確度是首要考量,請勿使用備用廣告。
/**
* 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();
}