# 性能与错误监控方案
# 背景
由于许多前端老项目需要性能优化,但是我们看不到用户实际数据,需要添加性能监控获取用户页面性能数据。同时许多项目需要用户页面错误数据,避免被动等待用户告诉我们bug。
# 方案
前端监控流程:数据采集 -> 数据上报 -> 服务端处理 -> 数据可视化
# 1. 性能数据采集
(1)页面信息采集。 window.performance.timing对象里包含了页面加载各个阶段的起始及结束时间。我们可以获取我们关心的几个属性:
// 白屏时间
whiteScreen: timing.domLoading - timing.navigationStart,
// 重定向耗时
redirect: timing.redirectEnd - timing.redirectStart,
// DOM 渲染耗时
dom: timing.domComplete - timing.domLoading,
// 页面加载耗时
load: timing.loadEventEnd - timing.navigationStart,
// 页面卸载耗时
unload: timing.unloadEventEnd - timing.unloadEventStart,
// 请求耗时
request: timing.responseEnd - timing.requestStart,
// 获取性能信息时当前时间
time: new Date().getTime(),
真正的白屏时间需要根据各自项目自定义计算,比如:从用户打开页面到首页接口返回数据并展示时间。
(2)资源加载信息采集。 通过window.performance.getEntriesByType('resource')方法可以获取页面当前加载的所有资源信息。它一般包括以下几个类型:script/link/img/css/fetch/other/xmlhttprequest,我们需要使用的信息有:
// 资源的名称
name: item.name,
// 资源加载耗时
duration: item.duration.toFixed(2),
// 资源大小
size: item.transferSize,
// 资源所用协议
protocol: item.nextHopProtocol,
# 2. 错误数据采集
页面经常捕捉的错误有6种:
- 资源加载错误。通过addEventListener('error', callback, true)进行捕捉。
- js执行错误。通过window.onerror捕捉js错误。
- promise错误。通过addEventListener('unhandledrejection', callback)捕捉promise错误。
- console.error错误。
- window.fetch请求错误。
- window.XMLHttpRequest请求错误。
还有vue框架里的错误Vue.config.errorHandler,需要自定义方法进行采集。
# 3. 性能数据上报
性能数据可以在页面加载完之后上报,不能对页面性能造成影响,可以在浏览器空闲时间进行采集并上报。这个功能不是所有浏览器都支持,需要做兼容判断。例如:
window.onload = () => {
if (window.requestIdleCallback) {
window.requestIdleCallback(() => {
report.performance = getPerformance()
report.resources = getResources()
reprotData()
})
} else {
setTimeout(() => {
report.performance = getPerformance()
report.resources = getResources()
reprotData()
}, 0)
}
}
# 4. 错误数据上报
错误数据上报跟性能数据上报为什么分开上报?性能数据跟错误数据不同,性能数据在页面加载完成时所有的数据已拿到手,而错误数据,是会在整个页面周期不断的收集数据。一个是一次性数据,一个是源源不断的数据。错误上报时机一般有延时上报和及时上报,但是用户一旦关系了浏览器传统处理方法就会上报失败。浏览器引入了navigator.sendBeacon方法。这个方法还是异步发出请求,但是请求与当前页面脱钩,作为浏览器的任务,因此可以保证会把数据发出去,不拖延卸载流程。
navigator.sendBeacon方法有以下特点:
(1)发出的是异步请求,并且是POST请求,后端解析参数时,需要注意处理方式;
(2)发出的请求,是放到的浏览器任务队列执行的,脱离了当前页面,所以不会阻塞当前页面的卸载和后面页面的加载过程,用户体验较好;
(3)只能判断出是否放入浏览器任务队列,不能判断是否发送成功;
(4)Beacon API不提供相应的回调,因此后端返回最好省略response body。
navigator.sendBeacon方法接受两个参数,第一个参数是目标服务器的 URL,第二个参数是所要发送的数据(可选),可以是任意类型(字符串、表单对象、二进制对象等)。使用方法:
window.unload = function() { // 在页面卸载的时候上报错误数据
navigator.sendBeacon('/xxxx', report.errors)
}