原文地址: segmentfault.com/a/119000001…
前端错误分类:js运行时错误、资源加载错误、接口错误
js运行时错误
js运行时的错误一般使用window.onerror捕获,但是有一种特殊情况就是promise被reject并且错误信息没有被处理的时候抛出的错误
一般情况的JS运行时错误
-
SyntaxError——解析时发生语法错误
window.onerror捕获不到SyntaxError,一般SyntaxError在构建阶段,甚至本地开发阶段就会被发现。
-
TypeError——值不是所期待的类型
-
ReferenceError——引用未声明的变量
-
RangeError——当一个值不在其允许的范围或者集合中
- 使用window.onerror和window.addEventListener('error')捕获。
window.onerror = function(msg, url, lineNo, columnNo, error){
//处理error信息
console.log(msg, url, lineNo, columnNo, error)
}
window.addEventListener('error', function(event){
console.log('addEventLister error:' , event)
}, true)
// true代表在捕获阶段调用,false代表在冒泡阶段捕获。使用true和false都可以
const a = 1
const a = 1
a = 2
console.log(a)
输出结果:
Uncaught (in promise)
- 当promise被reject并且错误信息没有被处理的时候,会抛出一个unhandledrejection,并且这个错误不会被window.error以及window.addEventListener('error')捕获,需要专门的window.addEventListener('unhandledrejection')捕获处理
window.addEventListener('unhandledrejection', function(event){
console.log("_________________________________________________")
console.log( event)
})
new Promise((resolve, reject) => {
throw new Error('123')
resolve()
})
new Promise((resolve, reject) => {
reject('321')
})
new Promise((resolve, reject) => {
reject('456')
}).then((value)=>{
}, (reason)=> {
console.log(reason)
})
结果输出:
console.error
- 一些特殊情况下,还需要捕获处理console.error,捕获方式就是重写window.console.error
var consoleError = window.console.error
window.console.error = function(){
console.log(JSON.stringify(arguments));
consoleError && consoleError.apply(window, arguments)
}
console.error('11111')
结果输出:
特别说明跨域日志
- 什么是跨域脚本error?
当加载不同域的脚本中发生语法错误时,为了避免消息泄露,语法错误的细节将不会报告,而代之简单的“script error”。在某些浏览器中,通过
<script>使用crossorigin属性并要求服务器发送适当的CORS HTTP响应头...
特别说明sourceMap
其他
- sentry把所有的回调函数使用try catch封装一层
- vue errorHandler 其原理也是使用try catch封装了nextTick, $emit, watch, data等
资源加载错误
使用window.addEventListener('error')捕获,window.onerror捕获不到资源加载错误。
window.addEventListener('error')捕获到的错误,可以通过target?.src || target?.href区分是资源加载错误还是js运行错误。
当一项资源(如图片或脚本)加载失败,加载资源的元素会触发一个Event接口的error事件,这些error事件不会向上冒泡到window,但能被捕获,而window.onerror不能检测捕获。
我们需要设置window.addEventListener()的第三个参数为true,才能捕获到资源加载错误。
window.addEventListener('error', function(e){
console.log('window.addEventListener error:', e)
},true)
接口错误
一般前端接口都是用jquery的Ajax请求,也有用fetch请求的,以及前端框架自己封装的请求等等。总之他们封装的方法各不相同,但是万变不离其宗,他们都是对浏览器的这个对象window.XMLHttpRequest进行封装,所以我们只要能够监听到这个对象的一些事件,就能够把请求的信息分离出来。
- 如何监听Ajax请求 通过监听XMLHttpRequest对象的两个事件loadstart、loadend。
function ajaxEventTrigger(event){
// 自定义事件
var ajaxEvent = new CustomEvent(event, {detail: this})
// dispatchEvent 向一个指定的事件目标派发一个事件;向window派发一个指定的事件
window.dispatchEvent(ajaxEvent)
}
var oldXHR = window.XMLHttpRequest;
function newXHR(){
var realXHR = new oldXHR()
realXHR.addEventListener('loadstart', function(){
ajaxEventTrigger.call(this, 'ajaxLoadStart')
}, false)
realXHR.addEventListener('loadend', function(){
ajaxEventTrigger.call(this, 'ajaxLoadEnd')
}, false)
return realXHR
}
const timeRecordArray = []
window.XMLHttpRequest = newXHR
window.addEventListener('ajaxLoadStart', function(e){
var tempObj = {
timeStamp : new Date().getTime(),
event: e,
simpleUrl: window.location.href.split('?')[0].replace('#',''),
uploadFlag: false // uploadFlag=true代表这个请求已经被上传过
}
timeRecordArray.push(tempObj)
})
window.addEventListener("ajaxLoadEnd", function(){
// ajaxLoadEnd时会修改ajaxLoadStart产生的e,两者影响的是同一个对象e
for (var i = 0; i < timeRecordArray.length; i ++) {
if(timeRecordArray[i].uploadFlag === true) continue
console.log("window.XMLHttpRequest addEventListener loadend: ", timeRecordArray[i].event.detail)
timeRecordArray[i].uploadFlag = true
}
})
- 如何监听fetch请求 通过第一种方法,已经能够监听到大部分的ajax请求。然而,使用fetch请求的人越来越多,因为fetch的链式调用可以让我们摆脱ajax的嵌套地狱,被更多的人所青睐。奇怪的是,使用第一种方式,却无法监听到fetch的请求事件,这是为什么呢?
由于fetch代码是内置在浏览器中,它必然先于监控代码执行,所以我们在添加监听事件的时候,是无法监听fetch里边的XMLHttpRequest对象的。
那怎么办呢,我们需要重写一些fetch的代码,只要在监控代码执行之后,我们重写一些fetch,就可以正常监听使用fetch方式发送的请求了。
如何重写fetch呢? 未完待续