前端监控主要分为以下几个方面
性能监控 采集以用户为中心的性能指标
错误监控 捕获代码错误和资源错误
行为监控 采集用户的点击事件、停留时间、ip, 清洗数据,描绘用户画像、pv、uv
数据上报 合适的方式上报数据给服务端
主要分为页面性能和资源性能
APIwindow.performance
requestAnimationFrame
测量浏览器帧率PerformanceObserver
订阅与性能相关的事件,回调在空闲期间触发elementtiming
具体监听某个元素的加载时间js
sendBeacon
API保证离线数据可靠上报Cache-Control: max-age=31536000
实现静态资源长效缓存<img>/<script>/<link>
等资源加载失败js
使用 window.onerror
+ window.addEventListener('error')
组合方案
js
unhandledrejection
事件捕获未处理的Promise拒绝js
js
路由变化
设备信息:
字段设计
json
js
性能数据可以在页面加载完成之后采集并上报,尽量不要对页面性能造成影响。或者requestIdleCallback
错误上报一般使用一般采用批量和立即上报2种方式
Xhr
Image
Sendbeacon
js
js
没有跨域问题
发 POST 请求之后不需要获取和处理数据、服务器也不需要发送数据
不会携带当前域名 cookie
不会阻塞页面加载,影响用户的体验,只需 new Image 对象
相比于 BMP/PNG 体积最小,可以节约 41% / 35% 的网络资源小
// 使用Performance API获取FCP
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
console.log('FCP:', entries[0].startTime);
});
observer.observe({type: 'paint', buffered: true});
document.addEventListener('error', (e) => {
if (e.target.tagName.toLowerCase() === 'img') {
reportError({
type: 'resource',
tag: e.target.tagName,
url: e.target.src,
status: e.target.naturalWidth === 0 ? '404' : 'unknown'
});
}
}, true);
window.addEventListener('error', (event) => {
const { message, filename, lineno, colno, error } = event;
reportError({
type: 'js',
message,
file: filename,
line: lineno,
column: colno,
stack: error?.stack
});
}, true); // 注意使用捕获阶段监听资源错误
window.addEventListener('unhandledrejection', (event) => {
reportError({
type: 'promise',
reason: event.reason?.message,
stack: event.reason?.stack
});
event.preventDefault(); // 阻止控制台默认报错
});
Vue.config.errorHandler = function (err) { setTimeout(() => { throw err }) }
window.onerror = function (message, url, line, column, error) { console.log(message, url, line, column, error); }
{
"timestamp": "2025-04-25T14:30:00Z",
"url": window.location.href,
"userAgent": navigator.userAgent,
"errorType": "js/promise/resource",
"message": "Cannot read property 'length' of undefined",
"stack": "at buttonClick (main.js:32:15)..."
}
const errorID = window.btoa(`${message}-${stack.split('\n')[0]}`);
window.onload = () => {
// 在浏览器空闲时间获取性能及资源信息
// https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback
if (window.requestIdleCallback) {
window.requestIdleCallback(() => {
monitor.performance = getPerformance()
monitor.resources = getResources()
})
} else {
setTimeout(() => {
monitor.performance = getPerformance()
monitor.resources = getResources()
}, 0)
}
}
window.unload = function() { // 在页面卸载的时候上报错误数据
navigator.sendBeacon('/xxxx', monitor.errors)
}
document.addEventListener('visibilitychange', function logData() {
if (document.visibilityState === 'hidden') {
navigator.sendBeacon('/log', analyticsData);
}
});
//onunload不要用,移动端有问题
//可以用pagehide来代替