js错误捕获
js错误的实质,也是发出一个事件,处理他
error实例对象
-
对象属性
- message:错误提示信息
- name:错误名称(非标准属性)宿主环境赋予
- stack:错误的堆栈(非标准属性)宿主环境赋予
-
对象类型(7种)
-
SyntaxError
对象是解析代码时发生的语法错误 -
ReferenceError
对象是引用一个不存在的变量时发生的错误 -
RangeError
对象是一个值超出有效范围时发生的错误(一是数组长度为负数,二是Number
对象的方法参数超出范围,以及函数堆栈超过最大值) -
TypeError
对象是变量或参数不是预期类型时发生的错误:对字符串、布尔值、数值等原始类型的值使用new
命令 -
URIError
对象是 URI 相关函数的参数不正确时抛出的错误:使用函数不当 -
eval
函数没有被正确执行时,会抛出EvalError
错误 - 不再使用,为了代码兼容
-
-
自定义错误
function UserError(message) { this.message = message || '默认信息'; this.name = 'UserError'; } UserError.prototype = new Error(); UserError.prototype.constructor = UserError; new UserError('这是自定义的错误!');
Js运行时错误处理机制
-
try..catch…finally
- 范围:用来捕获任何类型的同步错误,可以捕获async / await的代码,但无法捕获promise、setTimeout、dom回调(eg:onclick点击回调)的代码,在回调函数里面写try…catch可以捕获,但包在外面不会捕获,无法捕获语法错误
- 异步不捕获原因:
- async/await捕获原因:
-
window.onerror
- 范围:同步错误和异步错误都可以捕获,但无法捕获到静态资源异常,或者接口异常(网络请求异常不会事件冒泡,因此必须在捕获阶段将其捕捉到才行),无法捕获语法错误
- 原理:当
JS
运行时错误发生时,window
会触发一个ErrorEvent
接口的error
事件 -
参数
/** * @param {String} message 错误信息 * @param {String} source 出错文件 * @param {Number} lineno 行号 * @param {Number} colno 列号
*/
window.onerror = function(message, source, lineno, colno, error) {
console.log('捕获到异常:',{message, source, lineno, colno, error});
}
```
- 补充:window.onerror
函数只有在返回
true的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示
Uncaught Error: xxxxx -
onerror
最好写在所有JS
脚本的前面,否则有可能捕获不到错误;(捕获的是全局错误)
资源加载错误
- window.addEventListener(一项资源(如图片或脚本)加载失败,加载资源的元素会触发一个
Event
接口的error
事件,并执行该元素上的onerror()
处理函数,有浏览器兼容问题) -
注意:只能捕获无法冒泡
window.addEventListener('error', (error) => { console.log('捕获到异常:', error); }, true) // 一定要加true,捕获但不冒泡
-
Script error跨域的静态资源加载异常捕获(cdn文件等)
跨域文件只会报Script error,没有详细信息,怎么解决:
- 客户端:script标签添加crossOrigin
- 服务端:设置:Access-Control-Allow-Origin
<script src="http://jartto.wang/main.js" crossorigin></script>
iframe异常
-
使用window.onerror
<iframe src="./iframe.html" frameborder="0"></iframe> <script> window.frames[0].onerror = function (message, source, lineno, colno, error) { console.log('捕获到 iframe 异常:',{message, source, lineno, colno, error}); return true; }; </script>
promise异常捕获
- 没有写
catch
的Promise
中抛出的错误无法被onerror
或try-catch
捕获到 -
为了防止有漏掉的
Promise
异常,建议在全局增加一个对unhandledrejection
的监听,用来全局监听Uncaught Promise Error
window.addEventListener("unhandledrejection", function(e){ console.log(e); });
- 补充:如果去掉控制台的异常显示,需要加上:event.preventDefault();
vue异常捕获
-
VUE errorHandler
Vue.config.errorHandler = (err, vm, info) => { console.error('通过vue errorHandler捕获的错误'); console.error(err); console.error(vm); console.error(info); }
React异常捕获
- componentDidCatch(React16)
-
新概念Error boundary(React16):UI的某部分引起的
JS
错误不会破坏整个程序(只有 class component
可以成为一个error boundaries
)不会捕获以下错误
1.事件处理器 2.异步代码 3.服务端的渲染代码 4.在 error boundaries 区域内的错误
-
Eg: 全局一个
error boundary
组件就够用啦class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } componentDidCatch(error, info) { // Display fallback UI this.setState({ hasError: true }); // You can also log the error to an error reporting service logErrorToMyService(error, info); } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>Something went wrong.</h1>; } return this.props.children; } } <ErrorBoundary> <MyWidget /> </ErrorBoundary>
页面崩溃和卡顿处理
-
卡顿
- 网页暂时响应比较慢,
JS
可能无法及时执行 -
解决:
window
对象的load
和beforeunload
事件实现了网页崩溃的监控window.addEventListener('load', function () { sessionStorage.setItem('good_exit', 'pending'); setInterval(function () { sessionStorage.setItem('time_before_crash', new Date().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')); }
- 网页暂时响应比较慢,
-
崩溃
-
JS
都不运行了,还有什么办法可以监控网页的崩溃,并将网页崩溃上报呢 -
解决:
Service Worker
来实现网页崩溃的监控-
Service Worker
有自己独立的工作线程,与网页区分开,网页崩溃了,Service Worker
一般情况下不会崩溃; -
Service Worker
生命周期一般要比网页还要长,可以用来监控网页的状态; - 网页可以通过
navigator.serviceWorker.controller.postMessage API
向掌管自己的SW
发送消息。
-
-
错误上报机制
-
Ajax
发送数据
因为Ajax
请求本身也有可能会发生异常,而且有可能会引发跨域问题,一般情况下更推荐使用动态创建img
标签的形式进行上报。 - 动态创建
img
标签的形式 更常用,简单,无跨越问题
function report(error) {
let reportUrl = 'http://jartto.wang/report';
new Image().src = `${reportUrl}?logs=${error}`;
}
- 如果你的网站访问量很大,那么一个必然的错误发送的信息就有很多条,这时候,我们需要设置采集率,从而减缓服务器的压力:
Reporter.send = function(data) {
// 只采集 30%
if(Math.random() < 0.3) {
send(data) // 上报错误信息
}
}
js源代码压缩如何定位:成熟方案提供sentry
sentry 是一个实时的错误日志追踪和聚合平台,包含了上面 sourcemap 方案,并支持更多功能,如:错误调用栈,log 信息,issue管理,多项目,多用户,提供多种语言客户端等,
这里不过多叙述,之后在搭建sentry服务时,会再补篇博文
补充:node服务端错误处理机制
全栈开发,后端采用express库,在这里补充一下,node服务的错误处理方案
-
错误分类
- 一般错误处理:如某种回退,基本上只是说:“有错误,请再试一次或联系我们”。这并不是特别聪明,但至少通知用户,有地方错了——而不是无限加载或进行类似地处理
- 特殊错误处理为用户提供详细信息,让用户了解有什么问题以及如何解决它,例如,有信息丢失,数据库中的条目已经存在等等
-
步骤
-
1. 构建一个自定义 Error 构造函数:让我们方便地获得堆栈跟踪
class CustomError extends Error { constructor(code = 'GENERIC', status = 500, ...params) { super(...params) if (Error.captureStackTrace) { Error.captureStackTrace(this, CustomError) } this.code = code this.status = status } } module.exports = CustomError
-
2.处理路由:对于每一个路由,我们要有相同的错误处理行为
wT:在默认情况下,由于路由都是封装的,所以 Express 并不真正支持那种方式
解决:实现一个路由处理程序,并把实际的路由逻辑定义为普通的函数。这样,如果路由功能(或任何内部函数)抛出一个错误,它将返回到路由处理程序,然后可以传给前端
const express = require('express') const router = express.Router() const CustomError = require('../CustomError') router.use(async (req, res) => { try { const route = require(`.${req.path}`)[req.method] try { const result = route(req) // We pass the request to the route function res.send(result) // We just send to the client what we get returned from the route function } catch (err) { /* This will be entered, if an error occurs inside the route function. */ if (err instanceof CustomError) { /* In case the error has already been handled, we just transform the error to our return object. */ return res.status(err.status).send({ error: err.code, description: err.message, }) } else { console.error(err) // For debugging reasons // It would be an unhandled error, here we can just return our generic error object. return res.status(500).send({ error: 'GENERIC', description: 'Something went wrong. Please try again or contact support.', }) } } } catch (err) { /* This will be entered, if the require fails, meaning there is either no file with the name of the request path or no exported function with the given request method. */ res.status(404).send({ error: 'NOT_FOUND', description: 'The resource you tried to access does not exist.', }) } }) module.exports = router // 实际路由文件 const CustomError = require('../CustomError') const GET = req => { // example for success return { name: 'Rio de Janeiro' } } const POST = req => { // example for unhandled error throw new Error('Some unexpected error, may also be thrown by a library or the runtime.') } const DELETE = req => { // example for handled error throw new CustomError('CITY_NOT_FOUND', 404, 'The city you are trying to delete could not be found.') } const PATCH = req => { // example for catching errors and using a CustomError try { // something bad happens here throw new Error('Some internal error') } catch (err) { console.error(err) // decide what you want to do here throw new CustomError( 'CITY_NOT_EDITABLE', 400, 'The city you are trying to edit is not editable.' ) } } module.exports = { GET, POST, DELETE, PATCH, }
-
3.构建全局错误处理机制
process.on('uncaughtException', (error: any) => { logger.error('uncaughtException', error) }) process.on('unhandledRejection', (error: any) => { logger.error('unhandledRejection', error) })
-
总结:
1.可疑区域增加 Try-Catch
2.全局监控 JS
异常 window.onerror
3.全局监控静态资源异常 window.addEventListener
4.捕获没有 Catch
的 Promise
异常:unhandledrejection
5.VUE errorHandler
和 React componentDidCatch
6.监控网页崩溃:window
对象的 load
和 beforeunload
7.跨域 crossOrigin
解决
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。