React Error Catcher
React 内不同类型的谬误捕捉
本文将会从三个阶段来探讨产生在在 React 内的谬误捕捉,并且介绍如何封装一个通用的组件:
- React 内次要谬误根因和谬误捕捉办法
- 对捕捉谬误的数据处理
- 捕捉组件设计
Keyword:
React Error Boundary
ErrorEvent
Error Information
Todo:
- Error 展现和数据分析
我的项目地址:
- npm
- github
How did the error occur
在 React 内,一个谬误是如何产生的呢?
不如咱们先理解 JavaScript 内一些常见的谬误,这会在咱们开发组件时提供帮忙
// 一个简略的抛出异样,能够在脚本任何中央,会被 `onError` 捕捉const err = new Error('crash!')try { throw(err);} catch(e) { console.log(e);}// 利用 setTimeout 抛出异步谬误,渲染实现时触发componentDidMount() { setTimeout(() => { const e = new Error('111'); throw(e); }, 100);}// 因为用户交互引起的事件谬误,在编译时不被觉察,在执行时产生// `onError` 捕捉clickValue = (value: string) => { JSON.parse(JSON.parse(value));}// 异步事件谬误,即 Promise.reject()// `unhandledrejection` 捕捉(async (value: string) => { await JSON.parse(JSON.parse(value));})("hello")// 组件渲染谬误,咱们能够间接在 React JSX 内返回一个谬误render() { return ( <>new Error("hello")<\/> )}
在 React 内谬误依据其体现类型能够分为:
- 渲染谬误,即在渲染阶段产生异样,比方某个组件没有引入就应用
- 援用谬误,即引入某个资源文件时产生谬误,这个往往在编译过程中可能捕捉到。在这里咱们探讨异步引入的状况
- 事件处理,即渲染没问题,然而在在调用其触发事件时产生谬误,比方
JSON.parse(JSON.parse("some reason"))
,这类谬误能够细分为用户手动触发和脚本触发,能够参考Error.isTrusted
属性进行了解 - 异步代码,比方
promise.reject("some reason")
How to catch in React
捕捉谬误实际上就是弄清楚,when and where, who did what cause what(即什么人在什么工夫因为执行了某个文件的具体方法从而导致了某个谬误)
谬误和谬误的捕捉办法存在一对多的关系,即一个谬误可能被不同的办法捕捉,在 JavaScript 内谬误事件捕捉的办法各司其职:
- 应用
try {} catch(e){}
语句在事件处理器外部捕捉谬误 React Error boundaries
,能够捕捉资源援用和组件渲染谬误,通常这两者在 dev 时就能发现window.addEventListener('error')
,能够捕捉事件处理谬误window.addEventListener('unhandledrejection')
,能够捕捉异步代码谬误
接下来,咱们将从 谬误类型,捕捉机会和捕捉信息对这三种事件逐个进行介绍
Error boundaries
Error Boundaries 是 React 提出的一种用于谬误捕捉的组件,事实上,它是一个的定义了 static getDerivedStateFromError()
或 componentDidCatch()
生命周期办法的 class 组件
Error Boundaries 的呈现是为了解决:局部 UI 的谬误导致整个 App 的解体的问题,比方一个页面内,侧边栏呈现的问题导致整个页面无奈显示,此时能够通过捕捉侧边栏的谬误,渲染出降级的 UI(或者另一个计划)来防止这种状况
Error boundaries 能够捕捉并打印产生在其子组件树任何地位的 JavaScript 谬误并渲染出备用 UI
以下状况下不能通过 Error Boundaries 来 catch 谬误(然而能够通过上述其余伎俩来获取):
- 组件的外部的事件处理函数,因为 Error Boundaries 解决的仅仅是 Render 中的谬误,而 Handle Event 并不产生在 Render 过程中
- 异步函数中的异样,比方
setTimeout
或者setInterval
,requestAnimationFrame
等函数中的异样 - 服务端渲染
留神,Error Boundaries 仅捕捉其子组件中的谬误,自身的谬误无奈捕捉,这要求咱们在封装组件时,咱们须要对组件自身的谬误进行解决,使其可能捕捉并且不会陷入死循环
ErrorEvent of JavaScript
通过 onerror
捕捉的谬误类型为 ErrorEvent ,ErrorEvent 继承于 Event,其本身属性包含:
message: string
谬误的形容信息filename: string
产生谬误的文件名lineno: number
谬误产生的行号colno: number
谬误产生的列号error: Error
error 实例
持续理解 Event 获取更多信息,列举一些关键点:
Event.currentTarget
标识的是事件沿着 DOM 触发时的以后指标,它指向的是事件绑定元素(因为有可能在触发过程中被扭转),Event.target
指向的是事件触发元素isTrusted: boolean
示意事件是由浏览器(比方用户点击)发动(true)的还是由脚本(应用事件创立办法)引起的(false)timestamp
不同浏览器对其定义不统一,因而通常不要应用这个参数来作为工夫记录target.performance.timeOrigin
示意开始记录的高精度 timestamp
通过 onunhandledrejection
捕捉的谬误类型为 PromiseRejectionEvent ,它呈现在 JavaScript 内 promise
被 reject
时触发事件,其同样继承于 Event,其本身属性包含:
promise
reason
示意 promise 为什么被 rejected
Catched error data
针对不同路径获取的错误信息,进行解决后对立上报的信息,对于 onerror
和 onunhandledrejection
的信息通常通过 Event.target
提供,而对于 React Error Boundaries 捕捉的谬误,通常从 window
对象来获取
上面针对一些要害的错误信息进行梳理:
export interface ErrorInfo { app?: string // "onerror" | "onunhandledrejection" | "componentDidCatch" caughtEvent?: string user?: string message?: string // window.performance.timeOrigin 工夫戳 timeOrigin?: number | string // at filepath lineno:colno stack?: string // event.type 事件类型 type?: string // event.isTrusted 事件触发起源 isTrusted?: boolean // 是否启用 cookie cookieEnabled?: boolean cookie?: string userAgent?: string href?: string screenHeight?: number | string screenWidth?: number | string}
Create a component
当咱们具备了捕捉谬误的能力和整顿错误信息的能力之后,封装组件就是一件瓜熟蒂落的事件了,大家轻松封装出一个组件,因而我就交换一下在解决数据过程中的一些问题:
- 反复数据的解决。一个谬误往往可能在同一时间触发多个捕捉事件,咱们能够通过关键字段和
Set
类型来进行去重解决,我会抉择app
、user
、timeOrigin
和caughtEvent
组合成为一个标志符来进行判断 - 过滤指定谬误。因为通常谬误不是由咱们开发产生的,可能是因为引入的组件引起的,比方
antd
局部组件抛出的ResizeObserver loop limit exceeded
谬误,对于组件应用没有理论影响。对于这类谬误,咱们会提供过滤的性能,用来过滤掉指定的谬误。同时,为了避免组件自身外部引起的谬误循环上报,咱们能够抛出一个自定义的谬误,并将其进行默认过滤 - 批量上报。前端谬误的产生往往是在一瞬间同时产生的,因而咱们不得不思考进行批量上报。我会从工夫上(通过设置定时器工作)和数量上来进行束缚