1. JS Error 对象
在 Error 对象中主要有3个属性
- name 错误名称
- message 错误描述
- stack 错误的堆栈信息
请注意浏览器兼容问题,IE/Firfox/Chrome 差异。
2. JS 常见错误类型
- ReferenceError(引用错误)比如访问未声明的变量
- SyntaxError(语法错误)语法规则写错
- RangeError(范围错误) 比如死循环 Uncaught RangeError: Maximum call stack size exceeded
- 还有很多其他的错误类型
3. JS 如何捕获异常
3.1 常见的错误
- 资源加载报错
- JS 代码报错
- promise.reject 错误
- 非 promise.reject 错误
3.2 资源加载异常 (xxx loader error)
- 媒体资源加载
- CSS/JS 脚本资源
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>资源不存在异常</title>
<!-- main.css 不存在 -->
<link rel="stylesheet" href="./main.css">
<!-- main.js 不存在 -->
<script src="./main.js" async></script>
</head>
<body>
<!-- main.png 不存在 -->
<img src="./main.png" alt="">
</body>
<script>
// 捕获资源加载失败错误 js css img ...
addEventListener('error', e => {
const target = e.target
console.log({
type: target.localName,
url: target.src || target.href,
msg: (target.src || target.href) + ' load error',
time: new Date().getTime(),
})
}, true) // 注意是在捕获阶段,否则无法捕获错误
</script>
</html>
image.png
注意是在捕获阶段,否则无法捕获错误。
3.3 JS 代码报错 (非Promise.reject)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS异常(非Promise.reject)的捕获</title>
</head>
<body>
</body>
<script>
window.addEventListener('error', e => {
console.log(e.filename)
console.log(e.message)
console.log(e.error);
})
// 1. 可以捕获 ReferenceError: a is not defined
// console.log(a)
// 2. 可以捕获 Error: throw new Error
// throw new Error('throw new Error');
// 3. 不能捕获, 需要使用 unhandledrejection 事件来捕获
// Promise.reject('Promise.reject')
</script>
</html>
3.4 JS 代码报错 (Promise.reject)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS异常(Promise.reject)的捕获</title>
</head>
<body>
</body>
<script>
/**
* 不能获取 行号/列号/所属文件等信息
*/
window.addEventListener('unhandledrejection', e => {
console.log(e) // e 的 类型为 PromiseRejectionEvent
})
Promise.reject('this is a reject reason')
</script>
</html>
image.png
4. chrome JS异常解析
- unhandledrejection 事件异常类型,没有堆栈信息
- 资源加载 异常类型,没有堆栈信息
e.stack 第一行为错误描述,后面行为每一个 stack 的单独信息,sentry 中的 stacktrace 即通过解析 e.stack 而获得,解析逻辑为:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>chrome JS异常解析</title>
</head>
<body>
<h1>hello sentry</h1>
<button id="btn" onClick="doClick()">click</button>
</body>
<script src="https://browser.sentry-cdn.com/5.5.0/bundle.min.js" crossorigin="anonymous"></script>
<script>
// Sentry.init({ dsn: 'http://0a6466043b1548159093e55f0494c56f@localhost:9000/2' });
function doClick() {
try {
console.log(a)
} catch (e) {
resolveStack(e)
}
}
function resolveStack(e) {
const stackList = []
const list = e.stack.split('\n');
list.forEach((element, index) => {
if (index !== 0) {
const [fun, stackMsg] = element.replace(' at ', '').split(' ')
const [protol, filename, lineno, colno] = stackMsg.replace('(', '').replace(')', '').split(':');
stackList.push({
filename: protol + ':' + filename, // 文件名
colno: colno, // 列数
function: fun, // 调用函数
lineno: lineno // 行数
})
}
});
console.log(stackList)
}
</script>
</html>
5. 捕获跨域JS资源异常
跨域错误默认提示 Script error,而无法捕获到具体的错误信息:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body></body>
<script>
// 跨域错误默认提示 Script error
// 真实错误为:Uncaught ReferenceError: a is not defined
window.addEventListener('error', (e)=>{
console.log('error', e.message) // Script error
}, true);
</script>
<script src="http://demo.airtlab.com/lib/main.js"></script>
</html>
image.png
如何解决?客户端添加 crossorigin 属性,服务端添加 Access-Control-Allow-Origin: * 允许跨域:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body></body>
<script>
// 跨域错误默认提示 Script error
window.addEventListener('error', (e)=>{
console.log('error', e) //
}, true);
</script>
<script crossorigin="anonymous" src="http://demo.airtlab.com/lib/main.js"></script>
</html>
6. 本文总结
对于异常捕获有几个关键点:
- 资源加载异常的捕获,是在捕获阶段,否则无法捕获错误
- 资源加载异常 和 Promise.reject(unhandledrejection) 异常 没有堆栈信息
- 捕获跨域JS资源异常,需要特殊处理,客服端添加 crossorigin,服务端添加 CORS
网友评论