1. 常见异常种类
-
可疑区域增加 try...catch
-
全局监控JS异常: window.onerror
-
全局监控静态资源异常: window.addEventListener
-
全局捕获没有 catch 的 promise 异常:unhandledrejection
-
iframe 异常:window.error
-
errorCaptured
(Vue 3)或componentDidCatch
(React 18) -
监控网页崩溃:window 对象的 load 和 beforeunload
-
Script Error跨域 crossOrigin 解决
-
语法错误: 代码中存在语法错误,导致程序无法正确解析。
-
运行时错误:代码在运行时发生错误,如未定义变量、空指针引用等。
-
跨域错误:由于浏览器的同源策略,当页面尝试访问来自其他域名或端口的资源时,会发生跨域错误。
-
网络错误:包括网络请求失败、超时、断网等情况。
-
安全错误:涉及到恶意代码或网站,例如 XSS(跨站脚本攻击)、CSRF(跨站请求伪造)等问题。
-
性能问题:例如页面加载缓慢、卡顿等情况,可能由于过度渲染、大量 DOM 操作等原因引起。
-
兼容性问题:由于不同浏览器对 HTML/CSS/JS 的解释和支持程度不同,可能出现兼容性问题。
-
部署问题:包括文件路径错误、缺少文件等问题。
2. 如何处理异常
1. JS 语法错误、代码异常 try...catch
1. 同步异常
try {
const name = 'error'
console.log(nam)
} catch (error) {
console.log('捕获到异常', error)
}
2. 语法错误
删除一个引号,不过语法错误在我们开发阶段就可以看到,不会上到线上环境。
try {
const name = 'error
console.log(nam)
} catch (error) {
console.log('捕获到异常', error)
}
3. 异步异常
try {
setTimeout(() => {
console.log(nam)
})
} catch (error) {
console.log('捕获到异常', error)
}
2. 全局js异常 window.onerror
是JavaScript中的一个全局事件处理程序,用于捕获和处理未被捕获的异常和错误。
/**
* @param {String} message 错误信息
* @param {String} url 发生错误的脚本文件的URL
* @param {Number} line 行号
* @param {Number} column 列号
* @param {Object} error 错误对象:包含有关错误的详细信息的对象。堆栈跟踪:描述错误发生时函数调用堆栈的信息。
*/
try {
setTimeout(() => {
console.log(nam)
}, 1000)
} catch (error) {
console.log('捕获到异常', error)
}
window.onerror = function(message, url, line, column, error) {
console.log("Error message: " + message);
console.log("URL: " + url);
console.log("Line: " + line);
console.log("Column: " + column);
console.log("Error object: " + error);
console.log("Stack trace: " + error.stack);
}
- 同步错误 √
console.log(nam)
2. 语法错误 ×
let name = 'onerror
- 异步错误 √
setTimeout(() => {
console.log(nam)
}, 1000)
- 网络错误
<img src="./img.png" alt="" srcset="" />
不论是静态资源异常,或者接口异常,错误都无法捕获到。
注意: window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxxx
需要注意:
- onerror 最好写在所有 JS 脚本的前面,否则有可能捕获不到错误;
- onerror 无法捕获语法错误;
问题:静态资源加载异常怎么办?
3. 静态资源加载异常 window.addEventListener('error')
<img src="./img.png" alt="" srcset="" />
window.addEventListener('error', function(event) {
if (event.target.tagName === 'IMG' && event.type === 'error') {
console.log('图片加载失败:', event.target.src);
} else {
console.log('资源加载错误:', event.message, event.filename, event.lineno, event.colno, event.error);
}
event.preventDefault(); // 阻止默认的错误处理方式
}, true);
window.onerror和window.addEventListener('error')区别
都可以用来捕获JavaScript错误,但它们有一些区别:
- 触发时机:window.onerror事件只能捕获未被其他代码捕获的JavaScript错误,而window.addEventListener('error')可以捕获所有类型的JavaScript错误,包括未被其他代码捕获的错误和已被其他代码捕获但未被处理的错误。
- 错误信息:window.onerror事件只能获取到错误信息、错误文件、错误行号和错误列号等基本信息,而window.addEventListener('error')可以获取到更详细的错误信息,例如错误堆栈、错误类型等。
- 处理方式:window.onerror事件只能通过返回true或false来控制错误的处理方式,而window.addEventListener('error')可以通过event.preventDefault()方法来阻止默认的错误处理方式,并自定义处理方式。
- 兼容性:window.onerror事件在旧版本的浏览器中可能存在兼容性问题,而window.addEventListener('error')在大多数现代浏览器中都得到了良好的支持。
综上所述,window.addEventListener('error')比window.onerror更加灵活和强大,可以更好地捕获和处理JavaScript错误。
4. 全局捕获没有 catch 的 promise 异常:unhandledrejection
在 promise 中使用 catch 可以非常方便的捕获到异步 error ,这个很简单。
没有写 catch 的 Promise 中抛出的错误无法被 onerror 或 try-catch 捕获到,所以我们务必要在 Promise 中不要忘记写 catch 处理抛出的异常。
const p = new Promise((resolve, reject) => {
console.log(a)
})
被捕获了错误
p.catch(error => {
console.log('promise', error)
})
如果不写catch 添加全局捕获
window.addEventListener('unhandledrejection', function (e) {
console.log(e)
// 阻止向外扩散
// e.preventDefault()
})
5. iframe 异常:window.error
模拟不出来
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>iframe错误捕获</title>
</head>
<body>
<h1>iframe错误捕获</h1>
<iframe src="https://www.notexist.com"></iframe>
<script>
window.addEventListener('error', function(event) {
console.log('捕获到iframe错误:', event);
}, true);
</script>
</body>
</html>
捕获到iframe错误: ErrorEvent {isTrusted: true, message: "Load denied by X-Frame-Options: https://www.notexist.com does not permit cross-origin framing.", filename: "", lineno: 0, colno: 0, …}
6. errorCaptured
(Vue 3)或componentDidCatch
(React 18)
errorCaptured
(Vue 3)或componentDidCatch
(React 18)函数会被调用,我们可以在函数中记录错误信息、发送错误报告等
7. 监控网页崩溃:window 对象的 load 和 beforeunload
卡顿也就是网页暂时响应比较慢, JS 可能无法及时执行。但崩溃就不一样了,网页都崩溃了,JS 都不运行了,还有什么办法可以监控网页的崩溃,并将网页崩溃上报呢?
崩溃和卡顿也是不可忽视的,也许会导致你的用户流失。
- 利用 window 对象的 load 和 beforeunload 事件实现了网页崩溃的监控。不错的文章,推荐阅读:Logging Information on Browser Crashes。
window.addEventListener('load', function () {
sessionStorage.setItem('good_exit', 'pending');
setInterval(function () {
sessionStorage.setItem('time_before_crash', newDate().toString());
}, 1000);
});
window.addEventListener('beforeunload', function () {
sessionStorage.setItem('good_exit', 'true');
});
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'));
}
- 基于以下原因,我们可以使用 Service Worker 来实现网页崩溃的监控:
Service Worker 有自己独立的工作线程,与网页区分开,网页崩溃了,Service Worker 一般情况下不会崩溃;Service Worker 生命周期一般要比网页还要长,可以用来监控网页的状态;网页可以通过 navigator.serviceWorker.controller.postMessage API 向掌管自己的 SW 发送消息。