测页面的 FP(First Paint)、FCP(First Contentful Paint)、LCP(Largest Contentful Paint)、DOM Ready 这些关键性能指标,通常通过 Web API 和性能监控工具来实现。
1. FP(First Paint)和 FCP(First Contentful Paint)
FP 和 FCP 是页面加载过程中关键的初始渲染指标,通常使用 PerformanceObserver 监控这些时间点。
JavaScript 代码示例:
javascript
复制代码
const observer = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
entries.forEach((entry) => {
if (entry.name === 'first-paint') {
console.log('FP:', entry.startTime);
} else if (entry.name === 'first-contentful-paint') {
console.log('FCP:', entry.startTime);
}
});
});
observer.observe({ type: 'paint', buffered: true });
first-paint: 表示浏览器首次将任何内容绘制到屏幕的时间。first-contentful-paint: 表示浏览器首次绘制页面内容(如文本、图像)的时间。
2. LCP(Largest Contentful Paint)
LCP 测量的是页面中最大的可见元素(例如大图、块状内容)渲染的时间点。
JavaScript 代码示例:
javascript
复制代码
const observer = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
entries.forEach((entry) => {
console.log('LCP:', entry.startTime);
});
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });
LCP 是用户体验的关键性能指标之一,通常代表页面在用户视角下完成渲染的时间。
3. DOM Ready
DOM Ready 表示页面的 HTML 已经完全加载并解析,但外部资源(如图片、样式表)可能尚未加载完成。可以通过监听 DOMContentLoaded 事件来获取该时间点。
JavaScript 代码示例:
javascript
复制代码
document.addEventListener('DOMContentLoaded', () => {
const domReadyTime = performance.now();
console.log('DOM Ready:', domReadyTime);
});
综合方案:使用 Performance API 全面监控页面性能
通过 window.performance.timing 和 PerformanceObserver 可以获取更多关键指标。还可以结合 sendBeacon 或其他异步数据上报方式,将这些性能指标发送到后端,便于后续的分析和优化。
这些性能数据的采集,帮助开发者分析并优化页面加载体验。
requestAnimationFrame 可以用于检测页面渲染的时机,因此可以间接帮助我们检测 FMP(首次有意义渲染)。虽然 requestAnimationFrame 不能直接获取 FMP,但它能让我们准确地监控页面中重要元素的渲染时间,这对于检测 FMP 非常有帮助。
通过 requestAnimationFrame 检测 FMP 的实现思路
- 定义关键内容:页面上哪些内容是"有意义的"(如主要文字、图片等)。
- 监听这些内容的渲染:通过
requestAnimationFrame轮询页面的渲染状态,当关键内容出现在页面上时,我们记录渲染的时间。
示例代码
假设我们将页面中的 #main-content 作为主要内容,想检测该元素首次被渲染到屏幕的时刻:
javascript
复制代码
function detectFMP() {
const mainContent = document.querySelector('#main-content');
if (!mainContent) {
console.error('未找到关键元素');
return;
}
let fmpDetected = false;
// 递归调用 requestAnimationFrame 来不断检测内容是否渲染
function checkForFMP() {
// 检查是否已渲染(通过元素的尺寸和可见性)
const boundingRect = mainContent.getBoundingClientRect();
if (boundingRect.width > 0 && boundingRect.height > 0 && !fmpDetected) {
const fmpTime = performance.now();
console.log('首次有意义渲染(FMP)时间:', fmpTime);
fmpDetected = true; // 确保只记录一次
} else {
// 如果还没渲染,继续下一帧的检测
requestAnimationFrame(checkForFMP);
}
}
// 开始检测
requestAnimationFrame(checkForFMP);
}
// 当 DOM 内容加载完毕时,开始监控
window.addEventListener('DOMContentLoaded', detectFMP);
代码说明
-
detectFMP函数:- 通过
document.querySelector找到页面中关键的#main-content元素。 - 使用
requestAnimationFrame进行递归调用,每一帧都检查这个元素的渲染状态。
- 通过
-
requestAnimationFrame的作用:requestAnimationFrame是在浏览器每次重绘之前调用的函数,所以可以用于监控页面渲染的时机。- 通过持续检查
#main-content的尺寸(getBoundingClientRect),判断该元素何时被渲染到屏幕上。
-
FMP 的检测:
- 当
#main-content元素的宽度和高度都大于零,说明这个关键元素已经被渲染到页面上。 - 记录当前的时间(使用
performance.now()获取相对精确的时间),并将其作为 FMP 时间。
- 当
-
fmpDetected标志:- 为了确保 FMP 只被记录一次,使用一个标志位
fmpDetected,当检测到 FMP 后,将其设为true,避免重复记录。
- 为了确保 FMP 只被记录一次,使用一个标志位
下面是一个简单的心跳包机制的实现示例,使用 JavaScript 和 WebSocket。在这个示例中,客户端会定期发送心跳信号到服务器,并处理服务器的响应。
1. 客户端代码(JavaScript)
javascript
复制代码
const socket = new WebSocket('ws://yourserver.com/socket');
let heartBeatInterval;
const heartBeatTimeout = 5000; // 5秒的心跳间隔
const heartBeatResponseTimeout = 3000; // 3秒的响应超时
function startHeartBeat() {
heartBeatInterval = setInterval(() => {
if (socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({ type: 'heartbeat' }));
console.log('发送心跳包');
// 设置心跳响应超时
setTimeout(() => {
if (!responseReceived) {
console.error('未收到心跳响应,可能连接已断开');
socket.close();
}
responseReceived = false; // 重置响应标志
}, heartBeatResponseTimeout);
}
}, heartBeatTimeout);
}
let responseReceived = false;
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'heartbeatResponse') {
console.log('收到心跳响应');
responseReceived = true;
}
};
socket.onopen = () => {
console.log('连接已建立');
startHeartBeat();
};
socket.onclose = () => {
clearInterval(heartBeatInterval);
console.log('连接已关闭');
};
socket.onerror = (error) => {
console.error('WebSocket 错误:', error);
};