在介绍监控 SDK 前,我们首先先来看我们日常开发中的错误捕获机制。
Performance.timing,返回了 21 个只读属性,其中包含了页面加载的各种时间。
在写性能监控前,我们先来看基础的 api。
MDN:用于监测性能度量事件,在浏览器的性能时间轴记录下一个新的 performance entries 的时候将会被通知 。当然,也可以使用上面的 preformance.getEntriesByType()方法来得到对应 type 的数据。
performance.getEntriesByType(type);
// type: 'resource' | 'mark' | 'measure' | 'paint' | 'iframe/navigation'
接着,简单手写 SDK
主文件
async function Monitor(
logUrl = 'http://duanxl.com/log',
isPage = true,
isError = true
) {
// 是否需要上报
if (isPage) {
const PerForPage = await import('./perForPage');
new PerForPage(logUrl);
}
// 捕获错误
if (isError) {
const Catch = await import('./catch');
new Catch(logUrl);
}
}
export default Monitor;
base.js 用于上报
class Base {
constructor(logUrl, config) {
this.logUrl = logUrl;
this.config = config;
}
send() {
if (navigator.sendBeacon) {
navigator.sendBeacon(this.logUrl, this.config)
} else {
// new Image()
}
}
}
export default Base;
perForPage.js 性能监控
import Base from './base'
class PerForPage extends Base {
constructor() {
this._performance();
super(this.logUrl, this.config);
this.config = {};
}
_performance() {
let timing = performance.timing; //居然要废弃。。。将会有更强大的api来代替
this.config.performance = {
dns: timing.domainLookupEnd - timing.domainLookupStart,
// 重定向耗时
redirect: timing.redirectEnd - timing.redirectStart,
dom: timing.domComplete - timing.domLoading,
load: timing.loadEventEnd - timing.navigationStart,
unload: timing.unloadEventEnd - timing.unloadEventStart,
request: timing.responseEnd - timing.requestStart,
whiteScreen: timing.domLoading - timing.navigationStart,
// now time
time: Date.now()
}
const observer = new PerformanceObserver((list) => {
const lists = list.getEntries();
this.config.performance.fp = lists[0].duration;
this.config.performance.fcp = lists[0].duration;
this.send(this.logUrl, this.config);
});
observer.observe({ entryTypes: ['resource', 'paint'] });
}
}
export default PerForPage;
catch.js 错误收集
import Base from './base'
class Catch extends Base {
constructor() {
super(this.logUrl, this.config);
this.config = {};
this.catchError();
}
catchError() {
window.onerror((error)=>{
// ...
return true;
})
window.addEventListener('unhandledrejection',()=>{
// ...
return true;
})
window.addEventListener('error',(e)=>{
// ...
},true)
}
}
export default Catch;
xpath.js 录屏
let xpathArr = [];
// 录屏
window.addEventListener('click', (e) => {
xpathArr.push(e.target.id ?? e.target.className);
if (xpathArr.length >= 10) {
navigator.sendBeacon('https://duanxl.com/xpath', xpathArr.join('->'));
xpathArr = [];
}
})