You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
不过单单封装组件并不够,因为这样使用只能捕获到 jsx 中子组件的异常,不能捕获当前组件本身的异常,当前组件的异常需要到父组件中去处理。而在父组件中处理时,如果是直接在 jsx 根节点捕获,例如下面代码,如果组件 A 出现异常,会导致组件 B 和 C 都无法展示,扩大了异常的范围,这并不是我们所期望的:
构建鲁棒性:自定义 Eslint 规则增强 React 错误边界在前端应用中的控制力度
背景
在大型前端应用中,对于后端返回的非预期数据,常常出现难以预料的错误。前段时间我们出现了一次
p1
级别的故障,某个功能页面崩溃无法展示,经过排查发现是接口中的一个string
类型字段返回了非预期的null
,而前端在代码中使用了字符串的替换函数xxx.replace
,导致代码报错。这种情况不仅严重影响了用户体验,还会影响打工人的绩效(money)。经过复盘,我们可以采取一些技术手段,提高系统的鲁棒性和稳定性,从而降低再次出现p0
、p1
级别故障出现的概率。错误边界
错误边界是
React
官方提供的一种错误处理的解决方案,它可以在组件树的某个层级捕获子组件的JS
异常,并能够上报这些异常,同时展示降级 UI,从而有效地防止整个应用的崩溃。不过错误边界只能在类组件中使用,并且大部分情况遇到错误时处理逻辑都是一致的,因此常常会被封装成一个独立的组件来使用,例如:不过单单封装组件并不够,因为这样使用只能捕获到
jsx
中子组件的异常,不能捕获当前组件本身的异常,当前组件的异常需要到父组件中去处理。而在父组件中处理时,如果是直接在jsx
根节点捕获,例如下面代码,如果组件A
出现异常,会导致组件B
和C
都无法展示,扩大了异常的范围,这并不是我们所期望的:为了不扩大范围,代码则会变成下面这样,相信作为开发人员的你肯定无法忍受到处都是这样的代码:
因此,我们可以对
ErrorBoundary
组件再简单封装一层高阶组件(hoc),可以分别对A
、B
、C
三个子组件进行包裹:捕获粒度
利用错误边界可以将错误尽可能地控制在较小的范围,但是这个粒度也不是越细越好。粒度太粗了,会导致比较大的模块出现崩溃,影响用户体验,例如我们之前采用页面组件级别的粒度,就导致了整个页面崩溃,产生
p1
级别故障。粒度太细了,则会导致组件树层级过深,影响页面性能,造成页面卡顿。那么谁来决定哪些组件需要捕获,哪些不需要呢?Eslint 静态检查
对于上面的问题,我们认为应该由开发者和代码审核人员共同决定。这就需要提示开发者和审核者哪些组件用了错误边界,或者哪些组件没用。为了达到这个目的,我们可以编写一个
eslint
的自定义规则来检查代码,在提交代码和review
时进行提醒。实现
整体的思路是判断当前模块代码中有哪些
React
组件,遍历该组件以及该组件被引用的所有上层组件、高阶组件,判断是否有被errorBoundary
高阶组件包裹。判断 React 组件
React
组件大致有四种声明方式:class A extends Component {}
,对应的ast
节点是ClassDeclaration
function A() {}
,对应的ast
节点是FunctionDeclaration
const A = function() {}
,对应的ast
节点是FunctionExpression
const A = () => {}
,对应的ast
节点是ArrowFunctionExpression
但不是所有的类声明、函数声明、函数表达式都是
React
组件,也有可能是普通的类或函数。这里实现的思路是通过一个标志变量,在进入JSXElement
节点时标记为true
。在每个声明节点时初始化设为fasle
,如果在节点内部含有JSXElement
节点,则在声明节点出来时,这个标记就会为true
,那么它就是一个React
组件。当然由于规范组件必须是大写字母开头,因此如果节点名是小写时,可以直接跳过。当然,如果是一些纯逻辑组件,也有可能返回值是
null
,没有JSXElement
,这里我们倾向于在这些逻辑组件的父组件上进行错误捕获,因此跳过这种场景。遍历引用节点
拿到这些类或函数组件节点后,我们可以遍历该节点的所有引用节点,判断是否有被
errorBoundary
包裹,若没有包裹则进行提醒:判断是否被包裹
接入 lintstaged
首先在
eslint
配置文件中加入规则。注意我们这里是为了提醒,并不是所有组件都需要包裹,不可直接报错,因此规则值使用1
。然后在
lintstaged
配置文件中的eslint
命令加上规则路径参数--rulesdir ${errorBoundaryCheckRulePath}
既可。至此,每当开发人员提交
commit
时,如果有没被错误边界包裹的组件,就会进行提醒,效果如下:集成 CI 脚本
这里使用
node
命令脚本,接收jenkins
调用时传递的文件路径列表,读取每个文件的源码字符串,传递给eslint
进行检查,将检查的结果输出到日志中。最后jenkins
将日志读取出来放在pr
的页面进行展示。具体代码没有多少学习的价值,这里就不列了。结语
通过自定义
eslint
规则,我们成功让开发者有意识地将错误的影响范围缩小,提高了系统的鲁棒性,有效降低了p0
和p1
级别故障的概率。希望本次经验分享也能够对大家有所启发。前文
The text was updated successfully, but these errors were encountered: