不用try catch,如何机智的捕获错误
友情提醒:文末抽奖送4本《JavaScript高级程序设计》第四版
这是多个feature
组合使用后实现的神奇效果,在React
源码中被广泛使用。
当我读源码看到这里时,心情经历了:
懵逼 -- 困惑 -- 沉思 -- 查文档 -- 豁然开朗
看完此文,相信你也会发出感叹:
还能这么玩?
起源
我们知道,React
中有个特性Error Boundary
,帮助我们在组件
发生错误时显示“错误状态”的UI。
为了实现这个特性,就一定需要捕获到错误。
所以在React
源码中,所有用户代码
都被包裹在一个方法中执行。
类似如下:
function wrapper(func) {
try {
func();
} catch(e) {
// ...处理错误
}
}
比如触发componentDidMount
时:
wrapper(componentDidMount);
本来一切都很完美,但是React
作为世界级前端框架,受众广泛,凡事都讲究做到极致。
这不,有人提issue:
你们这样在
try catch
中执行用户代码
会让浏览器调试工具的Pause on exceptions
失效。
Pause on exceptions失效的来龙去脉
Pause on exceptions
是什么?
他是浏览器调试工具source
面板的一个功能。
开启该功能后,在运行时遇到会抛出错误的代码,代码的执行会自动停在该行,就像在该行打了断点一样。
比如,执行如下代码,并开启该功能:
let a = c;
代码的执行会在该行暂停。
这个功能可以很方便的帮我们发现未捕获的错误
发生的位置。
但是,当React
将用户代码
包裹在try catch
后,即使代码抛出错误,也会被catch
。
Pause on exceptions
无法在抛出错误的用户代码
处暂停,因为error
已经被React
catch
了。
除非我们进一步开启Pause on caught exceptions
。
开启该功能,使代码在捕获的错误
发生的位置暂停。
如何解决
对用户来说,我写在componentDidMount
中的代码明明未捕获错误,可是错误发生时Pause on exceptions
却失效了,确实有些让人困惑。
所以,在生产环境,React
继续使用try catch
实现wrapper
。
而在开发环境,为了更好的调试体验,需要重新实现一套try catch
机制,包含如下功能:
捕获
用户代码
抛出的错误,使Error Boundary
功能正常运行不捕获
用户代码
抛出的错误,使Pause on exceptions
不失效
这看似矛盾的功能,React
如何机智的实现呢?
如何“捕获”错误
让我们先实现第一点:捕获用户代码
抛出的错误。
但是不能使用try catch
,因为这会让Pause on exceptions
失效。
解决办法是:监听window
的error
事件。
根据GlobalEventHandlers.onerror MDN[1],该事件可以监听到两类错误:
js运行时错误(包括语法错误)。
window
会触发ErrorEvent
接口的error
事件资源(如
“在看和转发”就是最大的支持