确定网页最初是否在后台加载(例如,在新后台标签页中打开)对于准确监控性能至关重要。在后台加载的网页通常会延迟渲染,并且指标时间(例如 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();
}