js之前端异常日志

前端有哪些异常
  • JS 语法错误、代码异常
  • AJAX 请求异常
  • 静态资源加载异常
  • Promise 异常
  • Iframe 异常
  • 崩溃和卡顿
try-catch: 处理预见错误

1、try-catch只能捕获到同步的运行时错误。2、try-catch是监听可预见逻辑错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 同步错误: try-catch遇到同步错误会执行到catch
try{
let name = 'Ethan'
console.log(a11)
} catch (e) { console.log('能捕获异常:', e) } // 能捕获异常: ReferenceError: nam is not defined

// 语法错误:try-catch遇到js语法出错无法执行catch
try {
let name = 'jartto
console.log(nam);
} catch(e) {
console.log('不捕获到异常:',e); // Uncaught SyntaxError: Invalid or unexpected token
}

// 异步错误:try-catch遇到js异步出错无法执行catch
try {
setTimeout(() => {
undefined.map(v => v);
}, 1000)
} catch(e) {
console.log('捕获到异常:',e) // Uncaught TypeError: Cannot read property 'map' of undefined
}
window.onerror:监听同步异步和iframe错误

1、onerror可以捕获同步异步错误。2、onerror最好写在所有JS脚本的前面,否则有可能捕获不到错误。
3、当 JS 运行时错误发生时,window 会触发一个 ErrorEvent 接口的 error 事件,并执行 window.onerror()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
window.onerror = function(message, source, lineno, colno, error) {
// message:错误信息。source:发生错误的脚本URL。
// lineno:发生错误的行号。colno:发生错误的列号。
// error:Error对象。
console.log('捕获到异常:',{message, source, lineno, colno, error});
}

// 同步错误:window.onerror会被执行
a11

// 异步错误:window.onerror会被执行
setTimeout(() => {
Jartto;
});

// 语法错误: window.onerror没有被执行
let name = 'Ethan

// 网络请求:window.onerror没有被执行
<img src="./jartto.png">

window.addEventListener(‘error’)和window.onerror是同一种监听方式。当一项资源(如图片或脚本)加载失败,加载资源的元素会触发一个 Event 接口的 error 事件,并执行该元素上的onerror() 处理函数。这些 error 事件不会向上冒泡到 window ,所以必须在捕获阶段将其捕捉到才行,window.error默认为冒泡阶段,所以不能捕获。

1
2
3
4
5
6
7
8
9
<img src="./jartto.png">

window.addEventListener('error', (error) => {
if (error) {
let target = error.target || error.srcElement // 不同浏览器下返回的 error 对象不同
let isElementTarget = target instanceof HTMLScriptElement || target instanceof HTMLImageElement || target instanceof HTMLLinkElement
}
console.log('捕获到异常:', error);
}, true)
Script error另一种解决思路(仅参考)

改写了 EventTarget 的 addEventListener 方法。对传入的 listener 进行包装,返回包装过的 listener,对其执行进行 try-catch。

1
2
3
4
5
6
7
8
9
10
11
12
const originAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function (type, listener, options) {
const wrappedListener = function (...args) {
try {
return listener.apply(this, args);
}
catch (err) {
throw err;
}
}
return originAddEventListener.call(this, type, wrappedListener, options);
}
window.addEventListener(“unhandledrejection”): 捕获没有catch的Promise中的error

promise中catch可以非常方便的捕获到异步error,没有写catch的Promise中抛出的错误无法被onerror或try-catch捕获到

1
2
3
4
5
6
7
8
9
10
11
12
// 正常
new Promise((resolve, reject) => {
throw Error('e')
}).catch(e => {
console.log(e)
}) // 'e'

// 没有catch
window.addEventListener("unhandledrejection", function(e){ // 只有没有catch的时候promise报错才会执行(注意兼容性)
e.preventDefault() // 去掉控制台的异常显示
console.log(e);
});
页面崩溃和卡顿
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const time = null
// load事件触发代表页面中的 DOM,CSS,JS,图片已经全部加载完毕
// DOMContentLoaded 事件触发代表初始的 HTML 被完全加载和解析,不需要等待 CSS,JS,图片加载
window.addEventListener('load', function () {
sessionStorage.setItem('good_exit', 'pending');
time = setInterval(function () {
sessionStorage.setItem('time_before_crash', new Date().toString());
}, 1000);
});

window.addEventListener('beforeunload', function () {
sessionStorage.setItem('good_exit', 'true');
clearInterval(time)
});

if(sessionStorage.getItem('good_exit') &&
sessionStorage.getItem('good_exit') !== 'true') {
/* insert crash logging code here */
alert('Hey, welcome back from your crash, looks like you crashed on: ' + sessionStorage.getItem('time_before_crash'));
}
Vue捕获错误
1
2
3
4
5
6
Vue.config.errorHandler = (err, vm, info) => {
console.error('通过vue errorHandler捕获的错误');
console.error(err);
console.error(vm);
console.error(info);
}
React捕获错误

React 16 提供了一个内置函数 componentDidCatch,使用它可以非常简单的获取到 react 下的错误信息

1
2
3
componentDidCatch(error, info) {
console.log(error, info);
}

详细讲解

监听错误总结

在开发的过程中,对于容易出错的地方,可以使用try{}catch(){}来进行错误的捕获,做好兜底处理,避免页面挂掉。而对于全局的错误捕获,在现代浏览器中,使用使用window.addEventListener(‘error’),window.addEventListener(‘unhandledrejection’)就行了。如果需要考虑兼容性,需要加上window.onerror,三者同时使用。

参考教程

如何优雅处理前端异常
前端开发中的Error以及异常捕获