这篇文章是根据真传X前端进阶课程第四节课前端上课内容和我自己的一些感悟融合而成,事先声明以下,因为还没有在工作中正式使用过前端上报,如有错误,还请各位读者纠正。
这周老师讲的是前端上报,前端上报主要是前端收集数据、监控数据和上传错误日志。主要涉及到怎么上报、上报什么数据以及上报有什么好处,最后还有课上的答疑。
怎么上报
最简单的上报方式是使用 img 标签的 src 属性上报,在代码中只需要如下的代码中就可以了 new Image().url = 'your_report_url'
这种方式的优点就是简单,但是确定也很明显,浏览器对url的字符长度有限制,不能上报太过复杂的内容数据。
还有一种上报方式是使用 ajax 上报,相信 ajax 的用法大家已经很熟悉了。这种上报方式的优点是用 POST 请求上报时上报内容的长度没有限制,但是代码执行所在域与要上报的域不是同一个域的时候会有跨域限制。
上报什么数据
上报时带有数据才有意义,针对数据的不同类型及用途可分为以下几种:性能数据、用户行为数据、异常数据。
性能数据
性能数据是像DOM节点加载完成所用的时间之类的数据。获取这种数据的方法可以使用html5新增的 Performance API。
Performance 接口可以获取到当前页面中与性能相关的信息。它是 High Resolution Time API 的一部分,同时也融合了 Performance Timeline API、Navigation Timing API、 User Timing API 和 Resource Timing API。该类型的对象可以通过调用只读属性 Window.performance 来获得。
用户行为数据
用户行为数据,我理解的用户行为数据是像页面中某一块区域的点击量这样的数据。通常在该区域的页面点击事件处理函数中上报,需要注意的点是监听函数需要采用捕获方式,如果业务代码或第三方的代码禁用冒泡之后使用冒泡方式事件处理函数并不会被执行。
异常数据
异常数据是代码在运行过程中因为异常产生的异常信息,如异常堆栈信息等。
浏览器端监控异常的方法有:
- 可疑区域增加try-catch
- 全局监控JS异常 window.onerror
- 全局监控静态资源异常,window.addEventListener('error',fn)
- 捕获没有catch的Promise异常
- Vue errorHandler 和 React componentDidCatch
- 监控网页崩溃:window对象的load和beforeunload
try-catch使用try包裹容易产生异常的代码,在catch内对异常进行处理,缺点是各个可疑区域都要加try-catch,容易增加代码维护的难度。
对于try-catch方式监听异常,一般会重写可能会出现异常的代码,在这个代码的内部使用try-catch机制来捕获到异常,这样就不用频繁编写捕获异常的try-catch代码了。下面是一个小小的例子:
function init(){
throw new Error('this is a error')
}
var sysTimeout = window.setTimeout
window.setTimeout = function (fn, time) {
window.sysTimeout(function () {
try {
fn()
} catch (err) {
console.log(err)
}
}, time)
}
setTimeout(function () {
init()
}, 1000)
console.log('在前面')
在这个例子里面,执行函数 init 一定会抛出一个异常,定义了一个变量 sysTimeout 来保存系统定义的 setTimeout 函数,然后重写系统的 setTimeout 函数,在执行回调函数的时候添加try-catch捕获异常,最后在重写后的 setTimeout 回调函数内执行 init。
window.onerror 和 window.addEventListener('error',fn) 也都可以实现捕获代码中的异常,相对来说onerror要更简单一点,但是它的参数有5个,要获取完整的错误信息这5个参数不能省略。
// onerror
window.onerror = function (msg, url, lineNo, columnNo, error) {
console.log(msg)
console.log(url)
console.log(lineNo)
console.log(columnNo)
console.log(error)
}
// addEventListener
window.addEventListener('error', function (e) {
console.log(event.message)
console.log(event.filename)
console.log(event.lineno)
console.log(event.calno)
console.log(e.error.stack)
}, false)
当从其它的域加载的脚本运行出现异常后,虽然浏览器可以用红色字体指出异常信息,但是onerror捕获到的异常信息是 Script error.,即不能获取到具体的出错原因和错误堆栈信息。这是因为当加载自不同域的脚本中发生语法错误时,为避免信息泄露,语法错误的细节将不会报告,而代之简单的"Script error."
解决办法是为 script 标签添加 crossorigin 属性,然后在后端代码里面呢设置响应头 'Access-Control-Allow-Origin': '*',练习时可以使用 * 匹配所有的域,在生产环境一定要写明具体的域,防止被人获取隐私信息。
Promise所有未被 catch() 捕获处理的异常,都会被抛出为 unhandledrejection 异常事件,因此可以在全局使用 addEventListener 监听这个事件,对其做统一处理。事件对象event中的reason是抛出异常的原因。
window.addEventListener('unhandledrejection', (event) => {
console.log(event.reason)
}, false)
node端异常监控的方法有:
- 使用try-catch
- process.on('uncatchException',cb)
- domain - uncaughtException(Deprecated)
domain在node高版本已经被废弃,但还保留,可能是还没有想到更好的解决方案。
上报有什么好处
前端上报的一个好处是可以收集web页面运行时的数据,通过对优化前和优化后的性能数据进行比对,可以知道性能优化的结果是好是坏。
老师答疑
这里记录我觉得很重要的老师课上的答疑。
1、Q:有时会分PV和UV?
A: PV是次数,UV是个数。例如,我这个ID,一天点10次,它算10次PV。UV是用户级别的,是从用户维度区分,我这个用户一天点击算一个UV,10个用户点击了才算10个UV。
2、Q:百度统计和自己做得统计的区别?
A:
