一、性能监控概述
性能监控是优化网页性能、提升用户体验的关键步骤。通过监控页面的加载时间、资源加载、接口请求、用户交互、内存使用、页面卡顿等指标,开发者可以快速定位性能瓶颈并进行优化。
本文将介绍如何使用现代浏览器提供的 API(如 PerformanceNavigationTiming
、PerformanceObserver
等)实现全面的性能监控,并提供完整的代码示例。
二、核心性能监控指标
1. 页面加载性能
通过 PerformanceNavigationTiming
API,可以获取页面加载的关键时间点,包括:
- DCL(DOMContentLoaded) :DOM 加载完成的时间。
- Load:页面完全加载的时间。
- TTI(Time to Interactive) :页面可交互的时间。
- DNS 查询时间:DNS 解析耗时。
- TCP 连接时间:TCP 连接建立耗时。
- 请求响应时间:从发起请求到接收到响应的时间。
示例代码:
function getNavigationTiming() {
const [navigationEntry] = performance.getEntriesByType('navigation');
if (navigationEntry) {
const {
domContentLoadedEventEnd, // DCL
loadEventEnd, // Load
domInteractive, // TTI
domainLookupStart,
domainLookupEnd,
connectStart,
connectEnd,
requestStart,
responseStart,
responseEnd,
fetchStart,
} = navigationEntry;
console.log('DCL:', domContentLoadedEventEnd - fetchStart);
console.log('Load:', loadEventEnd - fetchStart);
console.log('TTI:', domInteractive - fetchStart);
console.log('DNS 查询时间:', domainLookupEnd - domainLookupStart);
console.log('TCP 连接时间:', connectEnd - connectStart);
console.log('请求响应时间:', responseEnd - requestStart);
}
}
window.addEventListener('load', getNavigationTiming);
2. 动态性能监控
通过 PerformanceObserver
API,可以实时监控以下动态性能指标:
- FCP(First Contentful Paint) :页面首次渲染内容的时间。
- LCP(Largest Contentful Paint) :页面最大内容渲染的时间。
- CLS(Cumulative Layout Shift) :页面布局偏移的累积值。
- TBT(Total Blocking Time) :页面主线程被长任务阻塞的总时间。
示例代码:
// FCP 监控
const fcpObserver = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
const fcpEntry = entries.find(entry => entry.name === 'first-contentful-paint');
if (fcpEntry) {
console.log('FCP:', fcpEntry.startTime);
}
});
fcpObserver.observe({ type: 'paint', buffered: true });
// LCP 监控
const lcpObserver = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
const lcpEntry = entries[entries.length - 1];
if (lcpEntry) {
console.log('LCP:', lcpEntry.renderTime || lcpEntry.loadTime);
}
});
lcpObserver.observe({ type: 'largest-contentful-paint', buffered: true });
// CLS 监控
let cls = 0;
const clsObserver = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
entries.forEach(entry => {
if (!entry.hadRecentInput) {
cls += entry.value;
console.log('CLS:', cls);
}
});
});
clsObserver.observe({ type: 'layout-shift', buffered: true });
// TBT 监控
let tbt = 0;
const longTaskObserver = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
entries.forEach(entry => {
const blockingTime = entry.duration - 50;
if (blockingTime > 0) {
tbt += blockingTime;
}
});
console.log('TBT:', tbt);
});
longTaskObserver.observe({ type: 'longtask', buffered: true });
3. 帧率(FPS)监控
帧率(Frames Per Second, FPS)是衡量页面流畅度的重要指标。通过 requestAnimationFrame
,可以实时计算页面的 FPS。
实现原理:
- 使用
requestAnimationFrame
循环调用监控函数。 - 记录每一帧的时间戳。
- 计算每秒的帧数(FPS)。
示例代码:
let fps = 0;
let frameCount = 0;
let lastTime = performance.now();
function checkFPS() {
const now = performance.now();
frameCount++;
if (now > lastTime + 1000) { // 每秒钟计算一次 FPS
fps = Math.round((frameCount * 1000) / (now - lastTime));
console.log(`当前 FPS: ${fps}`);
frameCount = 0;
lastTime = now;
}
requestAnimationFrame(checkFPS);
}
// 启动 FPS 监控
requestAnimationFrame(checkFPS);
4. 资源加载性能
监控页面中所有资源(如 CSS、JavaScript、图片、字体等)的加载时间和加载状态。
实现方法:
使用 PerformanceObserver
监听 resource
类型的性能条目。
示例代码:
const resourceObserver = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
entries.forEach(entry => {
console.log(`资源名称: ${entry.name}`);
console.log(`资源类型: ${entry.initiatorType}`);
console.log(`加载耗时: ${entry.duration.toFixed(2)}ms`);
console.log(`传输大小: ${(entry.transferSize / 1024).toFixed(2)} KB`);
console.log(`解码后大小: ${(entry.decodedBodySize / 1024).toFixed(2)} KB`);
console.log('-----------------------------');
});
});
// 开始监听资源加载
resourceObserver.observe({ type: 'resource', buffered: true });
5. 接口请求性能
监控页面中所有接口请求的响应时间、成功率和错误状态。支持 fetch
和 XMLHttpRequest
。
5.1 监控 fetch
请求
实现方法:
重写 fetch
方法,拦截所有 fetch
请求。
示例代码:
const originalFetch = window.fetch;
window.fetch = function (...args) {
const startTime = performance.now();
return originalFetch.apply(this, args)
.then(response => {
const duration = performance.now() - startTime;
console.log(`fetch 请求 ${args[0]} 耗时: ${duration}ms`);
return response;
})
.catch(error => {
console.error(`fetch 请求 ${args[0]} 失败:`, error);
throw error;
});
};
5.2 监控 XMLHttpRequest
请求
实现方法:
重写 XMLHttpRequest
的 open
和 send
方法,拦截所有 XMLHttpRequest
请求。
示例代码:
// 简单版
const originalXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function () {
const xhr = new originalXHR();
const startTime = performance.now();
xhr.addEventListener('load', function () {
const duration = performance.now() - startTime;
console.log(`XMLHttpRequest 请求 ${this.responseURL} 耗时: ${duration}ms`);
});
return xhr;
};
// 全面版
const originalXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function () {
const xhr = new originalXHR();
let startTime;
// 重写 open 方法
const originalOpen = xhr.open;
xhr.open = function (method, url) {
this._method = method;
this._url = url;
originalOpen.apply(this, arguments);
};
// 重写 send 方法
const originalSend = xhr.send;
xhr.send = function (body) {
startTime = performance.now(); // 记录请求开始时间
this.addEventListener('load', onRequestComplete);
this.addEventListener('error', onRequestError);
this.addEventListener('abort', onRequestAbort);
originalSend.apply(this, arguments);
};
// 请求完成时的回调
function onRequestComplete() {
const duration = performance.now() - startTime;
console.log(`XMLHttpRequest 请求 ${this._method} ${this._url} 耗时: ${duration}ms`);
this.removeEventListener('load', onRequestComplete);
this.removeEventListener('error', onRequestError);
this.removeEventListener('abort', onRequestAbort);
}
// 请求失败时的回调
function onRequestError() {
console.error(`XMLHttpRequest 请求 ${this._method} ${this._url} 失败`);
this.removeEventListener('load', onRequestComplete);
this.removeEventListener('error', onRequestError);
this.removeEventListener('abort', onRequestAbort);
}
// 请求被取消时的回调
function onRequestAbort() {
console.warn(`XMLHttpRequest 请求 ${this._method} ${this._url} 被取消`);
this.removeEventListener('load', onRequestComplete);
this.removeEventListener('error', onRequestError);
this.removeEventListener('abort', onRequestAbort);
}
return xhr;
};
6. 用户交互性能
监控用户点击、滚动、输入等操作的响应时间,以及长任务对交互的影响。
6.1 监控点击事件响应时间
实现方法:
监听 click
事件,记录从事件触发到回调函数执行完成的时间。
示例代码:
document.addEventListener('click', function () {
const startTime = performance.now();
requestAnimationFrame(() => {
const duration = performance.now() - startTime;
console.log(`点击事件响应时间: ${duration}ms`);
});
});
6.2 监控滚动事件响应时间
实现方法:
监听 scroll
事件,记录从事件触发到回调函数执行完成的时间。
示例代码:
window.addEventListener('scroll', function () {
const startTime = performance.now();
requestAnimationFrame(() => {
const duration = performance.now() - startTime;
console.log(`滚动事件响应时间: ${duration}ms`);
});
});
6.3 监控输入事件响应时间
实现方法:
监听 input
事件,记录从事件触发到回调函数执行完成的时间。
示例代码:
document.addEventListener('input', function () {
const startTime = performance.now();
requestAnimationFrame(() => {
const duration = performance.now() - startTime;
console.log(`输入事件响应时间: ${duration}ms`);
});
});
7. 内存使用情况
监控页面的内存占用情况,检测内存泄漏。
实现方法:
使用 performance.memory
(仅限 Chrome)获取内存信息。
示例代码:
if (performance.memory) {
console.log(`内存使用: ${performance.memory.usedJSHeapSize} bytes`);
}
8. 页面卡顿检测
监控页面是否出现卡顿(如 FPS 低于 30),并记录卡顿的持续时间。
实现方法:
结合 requestAnimationFrame
和 FPS 计算,检测卡顿。
示例代码:
let lastTime = performance.now();
let frameCount = 0;
let isStuttering = false;
function checkStutter() {
const now = performance.now();
frameCount++;
if (now > lastTime + 1000) {
const fps = Math.round((frameCount * 1000) / (now - lastTime));
if (fps < 30) {
isStuttering = true;
console.warn(`页面卡顿,FPS: ${fps}`);
} else if (isStuttering) {
isStuttering = false;
console.log('页面恢复正常');
}
frameCount = 0;
lastTime = now;
}
requestAnimationFrame(checkStutter);
}
requestAnimationFrame(checkStutter);
9. 自定义业务指标
监控关键业务操作的耗时(如表单提交、页面跳转)以及用户关键路径的性能(如从登录到下单的耗时)。
实现方法:
在业务代码中手动埋点,记录关键操作的时间。
示例代码:
function trackBusinessOperation(operationName) {
const startTime = performance.now();
return {
end: () => {
const duration = performance.now() - startTime;
console.log(`${operationName} 耗时: ${duration}ms`);
},
};
}
const operation = trackBusinessOperation('表单提交');
// 业务逻辑...
operation.end();
三、总结
通过以上方法,您可以全面监控页面的性能,包括:
- 页面加载性能:DCL、Load、TTI、DNS 查询时间、TCP 连接时间、请求响应时间。
- 动态性能监控:FCP、LCP、CLS、TBT。
- 帧率监控:实时计算 FPS。
- 资源加载性能:监控所有资源的加载时间和状态。
- 接口请求性能:监控
fetch
和XMLHttpRequest
的响应时间、成功率和错误状态。 - 用户交互性能:监控点击、滚动、输入等操作的响应时间。
- 内存使用情况:监控内存占用和内存泄漏。
- 页面卡顿检测:检测页面卡顿并记录持续时间。
- 自定义业务指标:监控关键业务操作的耗时。
这些监控点可以帮助您更好地优化页面性能,提升用户体验。