随着React16的发布越来越接近,我们想宣布一些关于在组件内如何处理JavaScript错误的变化。这些变化包括在React16 Beta版本,并将会成为React16的一部分。
顺便说一句, 我们刚刚发布了第一个React16 beta让你尝试!(https://github.com/facebook/react/issues/10294)
在过去,组件内部的JavaScript错误会破坏React的内部状态,并导致它在下一步的渲染中触发神秘错误 。这些错误经常是由代码中早期的错误引起的,但是React并没有提供一种在组件中优雅地处理它们的方法,并且无法从它们中恢复过来。
UI部分的一个JavaScript错误不应该破坏整个程序。为了给React用户解决这个问题,React16引入了“错误边界”的新概念。
错误边界是在他们的子组件树中捕捉JavaScript错误,记录这些错误,并显示一个回退UI的React组件,而不是崩溃的组件树。错误边界捕捉渲染过程中、生命周期方法中以及它们下面整个树的构造函数中的错误。
如果一类组件定义了一个新的生命周期方法 componentDidCatch(error,info)
,那么这类组件就成为一个错误边界:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// 显示回退UI
this.setState({ hasError: true });
// 你也可以把错误信息上报给错误上报服务器
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// 你可以渲染任何自定义的回退UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
然后就可以作为常规组件使用它:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
这个 componentDidCatch()
方法就像JavaScript中的 catch{}
块,但它是应用于组件上的。只有组件类可以成为错误边界。实际上,大多数情况下您希望声明一次错误边界组件,并在整个应用程序中使用它。
注意,错误边界只能捕获树结构中它下面组件中的错误。一个错误边界不能捕获它本身的错误。如果错误边界捕获错误失败,则错误将传播到上面最接近的错误边界。这也类似于JavaScript中 catch{}
块的工作原理。
查看这个声明的例子和使用错误边界(https://codepen.io/gaearon/pen/wqvxGa?editors=0010)与React 16 beta(https://github.com/facebook/react/issues/10294)。
错误边界的粒度取决于您。您可以包装顶层路由组件来向用户显示“出错”消息,就像服务器端框架经常处理崩溃一样。您还可以将单个小组件封装在错误边界中,以保护它们不致破坏应用程序的其余部分。
这一变化具有重要意义。对于React16,没有被任何错误边界捕获的错误将导致整个React组件树的卸载。
我们讨论了这个决定,但根据我们的经验,把损坏的UI留下比彻底删除更糟糕。例如,在像Messenger这样的产品中,留下破损的UI可能导致某人向错误的人发送消息。同样,对于一个支付应用程序显示错误的金额比什么都不渲染要坏。
这种变化意味着,当您迁移到React16时,您可能会发现以前应用程序中没有注意到的错误崩溃。添加错误边界,可以在出错时,提供更好的用户体验。
例如,Facebook Messenger将边栏、信息面板、会话日志和消息输入的内容封装到不同的错误边界中。如果某个UI区域中的某个组件崩溃,剩下的部分仍然保持交互。
我们也鼓励您使用JS错误上报服务(或建立您自己的),您可以了解他们在生产中发生的未处理的异常,并修复。
在开发过程中,React16会将渲染过程中发生的所有错误打印到控制台,即使应用程序意外地将它们删除。除了错误消息和JavaScript的栈,它也提供了组件的堆栈跟踪。现在你可以精确地看到在组件树的哪部分发生了错误:
你也可以看到文件名和行号在组件堆栈跟踪中。这在Create React App脚手架中是默认的:
如果你不使用Create React App,你可以添加这个插件手动修改你的Babel配置。请注意,它只是为了在开发过程中使用,在生产环境一定要禁止。
try
/ catch
?try
/ catch
很伟大,但是它只适用于必要的代码:
try {
showButton();
} catch (error) {
// ...
}
然而,React组件是声明和指定什么内容应该呈现:
`<Button />`
错误边界保留了React的声明性,并按您预期的方式运行。例如,即使一个错误发生在 componentDidUpdate
,但是它是由组件树深处的某个 setState
造成的,它仍然会正确地传播到最近的错误边界。
React15包含一个对错误边界支持很有限的方法,它有一个不同的名字: unstable_handleError
。这种方法不再工作,从最初的16 beta版本开始,您需要在代码中把它改为 componentDidCatch
。
对于这种变化,我们提供了a codemod(https://github.com/reactjs/react-codemod#error-boundaries)来自动迁移您的代码。
往期精选文章 |
---|
使用虚拟dom和JavaScript构建完全响应式的UI框架 |
扩展 Vue 组件 |
使用Three.js制作酷炫无比的无穷隧道特效 |
一个治愈JavaScript疲劳的学习计划 |
全栈工程师技能大全 |
WEB前端性能优化常见方法 |
一小时内搭建一个全栈Web应用框架 |
干货:CSS 专业技巧 |
四步实现React页面过渡动画效果 |
让你分分钟理解 JavaScript 闭包 |
小手一抖,资料全有。长按二维码关注京程一灯,阅读更多技术文章和业界动态。