最近在项目编写过程中发现经常会因为接口问题抛出各种各的错误,有的时候页面甚至会直接白屏,非常影响用户体验。为此,我仔细研究了一下React处理异常的方案,下面我将开始我的分享。

错误边界的作用

  • 前端异常监控:通过错误捕获,发送到服务器,进行错误监控

  • 页面崩溃跳转:当页面崩溃时会直接白屏,通过错误边界捕获并显示预先写好的错误页面,给予用户提示,还可以提供跳转按钮刷新页面

常规的错误边界处理方式:

  • window.onerror

  • React16+提供的错误边界解决方案

window.onerror

作用

  • 捕获抛出的所有错误

代码实现

  window.onerror = function (msg, url, line) {message.error('页面发生错误,即将重新刷新页面');setTimeout(() => {window.location.reload();}, 3000);return true;};

分析:
window.onerror捕获当前错误的各种信息,报错错误内容,错误发生所在行等等,同时返回值是一个布尔值,true代表在console中打印,false代表不打印。

React提供的ErrorBoundary

作用

  • 捕获发生在错误边界内的错误

  • 渲染备用UI

可捕获的异常范围

  • 子组件的渲染错误

  • 生命周期函数内的错误

  • 构造函数内的错误

不可捕获的异常

  • 事件处理函数内的错误(如onClick函数中的错误)

  • 异步代码中的错误(如setTimeout回调函数中的错误)

  • 服务端渲染

  • 错误边界代码自身的错误

代码实现

注: React提供的错误边界的解决方案只能通过class组件实现,hooks不支持,因为要使用到特定的生命周期

import React from 'react';import { message, Button } from 'antd';import { history } from 'umi';import styles from './index.less';class ErrorBoundary extends React.Component {constructor(props) {super(props);this.state = { hasError: false };}static getDerivedStateFromError(error) {message.error(error);return { hasError: true };}componentDidCatch(error, errorInfo) {message.error(error);message.error(errorInfo);}render() {if (this.state.hasError) {return (<div className={styles.container}><h1>页面开了会儿小差...</h1><div className={styles['btn-wrapper']}><div className={styles.btn}><Buttontype="primary"onClick={() => {history.push('/');}}>                返回主页</Button></div></div></div>);}return this.props.children;}}export default ErrorBoundary;

分析: 上面的代码可以分为三块,下面逐块分析


 static getDerivedStateFromError(error) {message.error(error);return { hasError: true };}

这是react提供的一个特定的生命周期方法,用来渲染备用UI。此处的hasError是ErrorBoundary组件内的一个state,如上所示,通过设定它的值为true来切换备用UI


 componentDidCatch(error, errorInfo) {message.error(error);message.error(errorInfo);}

这是react提供的另一个声明周期方法,用来打印错误信息。如上图所示,通过message或者console将当前捕获的错误信息打印出来


if (this.state.hasError) {return (<div className={styles.container}><h1>页面开了会儿小差...</h1><div className={styles['btn-wrapper']}><div className={styles.btn}><Buttontype="primary"onClick={() => {history.push('/');}}>               返回主页</Button></div></div></div>);}return this.props.children;

这里就是备用UI了。根据hasError的状态,如果为true说明捕获到错误了,显示准备好的UI;如果为false,则正常显示ErrorBoundary包裹的子组件,值得一提的是,ErrorBoundary组件写好后,一般作为最顶层组件,包裹我们的业务组件,具体代码如下所示:

<ErrorBoundary><div>这里是实际业务组件</div></ErrorBoundary>

React不可捕获的异常测试

前置知识:

在开发环境下,如果报错会弹出一个错误提示页面,以帮助开发者发现问题解决问题,如下图所示:

拉到最下面,可以看到这样一串文字:

This screen is visible only in development. It will not appear if the app crashes in production.
Open your browser’s developer console to further inspect this error. Click the 'X' or hit ESC to dismiss this message.

他告诉我们可以通过点击右上角的X关掉这个提示,显示出页面崩溃的真实模样。

明白这一点才可以正常进行我们的测试,下面正式开始测试

测试手段:

直接通过在代码中写入throw new Error('测试错误边界');来进行异常抛出

公用的错误边界组件:

import React from 'react';class ErrorBoundary extends React.Component {constructor(props) {super(props);this.state = { hasError: false };}static getDerivedStateFromError(error) {console.log(error);return { hasError: true };}componentDidCatch(error, errorInfo) {console.log(error);console.log(errorInfo);}render() {if (this.state.hasError) {return <h1>页面开了会儿小差...</h1>;}return this.props.children;}}export default ErrorBoundary;

备用UI:

测试项目:

  1. 事件处理函数内的错误

// 顶层组件import React from 'react';import ErrorBoundary from './component/ErrorBoundary/index'import ErrorBoundaryTest from './pages/ErrorBoundary/Child/index'function App() {return (<ErrorBoundary><ErrorBoundaryTest /></ErrorBoundary>);}export default App;
// 测试组件import React from 'react';function ErrorBoundaryTest() {return (<div onClick={() =>{throw new Error('测试错误边界');// 在这里,div的onClick事件中抛出异常}}>这里是子组件 </div> );}export default ErrorBoundaryTest;

结果图:

分析:
由上两张结果图可以看出,点击div确实触发了onClick时间并抛出了异常,但是并没有显示备用UI,即点击了错误页面右上角的X以后,并没有显示“页面开了会儿小差...”。由此可以看出,React的ErrorBoundary解决方案确实无法应对事件处理函数内的错误。

  1. 异步代码中的错误

顶层代码相同,不再重复书写

// 测试组件import React from 'react';function ErrorBoundaryTest() {setTimeout(() => { // 在这里写好定时,定5S后自动抛出异常throw new Error('测试错误边界');}, 5000)return (<div onClick={() =>{// throw new Error('测试错误边界');}}>这里是子组件 </div> );}export default ErrorBoundaryTest;

结果图:

分析:
经过五秒的定时,同样抛出了异常,但同样ErrorBoundary并未捕获到。

  1. 错误边界代码自身的错误
    公共ErrorBoundary组件做出一点点修改,改为该组件自身抛出异常。

// 公共ErrorBoundary组件import React from 'react';class ErrorBoundary extends React.Component {constructor(props) {super(props);this.state = { hasError: false };}static getDerivedStateFromError(error) {console.log(error);return { hasError: true };}componentDidCatch(error, errorInfo) {console.log(error);console.log(errorInfo);}render() {throw new Error('测试错误边界'); // 在这里让错误边界自身发生错误if (this.state.hasError) {return <h1>页面开了会儿小差...</h1>;}return this.props.children;}}export default ErrorBoundary;
// 顶层组件import React from 'react';import ErrorBoundary from './component/ErrorBoundary/index'import ErrorBoundaryTest from './pages/ErrorBoundary/Child/index'function App() {return (<ErrorBoundary><ErrorBoundaryTest /></ErrorBoundary>);}export default App;
// 子组件import React from 'react';function ErrorBoundaryTest() {//   setTimeout(() => {//   throw new Error('测试错误边界');// }, 5000)return (<div onClick={() =>{// throw new Error('测试错误边界');}}>这里是子组件 </div> );}export default ErrorBoundaryTest;

结果图:

分析:
一如既往,抛出了异常但并未显示备用UI,不同的是,这次异常直接引发了白屏。

总结:

经过上面缜密的测试,确实发现react错误边界的捕获范围有限。想要完整的捕获异常,需同时辅以window.onerror,捕获到所有的错误并进行相应处理。

react跳转到网络异常页面_React错误边界处理相关推荐

  1. react如何刷新当前页面_react 怎么刷新页面?

    react 怎么刷新页面? react 项目中页面跳转, 刷新及获取网络状态// 页面跳转 Windows.location.href='http://speedtest.wangxiaotong.c ...

  2. react 错误边界_React with GraphQL和错误边界中的自定义错误页面

    react 错误边界 by Abi Noda 通过Abi Noda React with GraphQL和错误边界中的自定义错误页面 (Custom error pages in React with ...

  3. J2EE中在web.xml异常页面跳转

    我们知道如何在struts中如何实现异常页面的跳转,那么如何在web.xml处理异常呢? <!-- 控制器中的异常处理 --> <!-- 403  禁止访问 -->  < ...

  4. react跳转url,跳转外链,新页面打开页面

    react中实现在js中内部跳转路由,有两种方法. 方法一: import PropTypes from 'prop-types'; export default class Header exten ...

  5. springMVC异常处理器:自定义异常处理器捕获系统异常,控制异常页面跳转

    首先看一个异常页面 404/500可能是大家最熟悉的两个错误代码,在传统方式下,代码遇到类如1/0这样的异常时,我们可以用try-catch捕获,交给前端控制器处理,如果前端控制器没有规范好异常处理器 ...

  6. mvc ajax异常,使用SpringMVC的controller中能获取数据但直接跳到异常页面,使用Ajax。...

    Controller代码 import java.util.Date; import org.springframework.stereotype.Controller; import org.spr ...

  7. nuiapp请求网络_uni-app 页面配置和跳转(一)

    今天看Dcloud官网更新了个uni-app,据说一套代码三端发布(Android,iOS,微信小程序),果断一试. uni.navigateTo(OBJECT) 保留当前页面,跳转到应用内的某个页面 ...

  8. 快递100 官方api技术文档 错误 更新 快递公司网络异常 解决方法

    http://www.kuaidi100.com/openapi/api_post.shtml 1.应用场景 (1)电商网站用户打开"我的订单"时调用此API显示结果 (2)物流系 ...

  9. php框架全局自定义错误,[TP笔记]ThinkPHP自定义错误页面、成功页面及异常页面

    看了前面分享的一篇<什么是真正的程序员?>,觉得自己离这个目标还差的很远,所以要抓紧朝着这个方向前进.作为一名PHP小码农,掌握一种框架是必须的.所以今天开始我们就从国产PHP框架 Thi ...

最新文章

  1. ssh,FTP到远程服务器时,显示自定义的警告信息
  2. 使用Java反射更改私有静态最终字段
  3. Effective 笔记
  4. 下一代微服务(service Mesh)
  5. Linux下查看出口IP
  6. Linux 字符设备驱动结构(四)—— file_operations 结构体知识解析
  7. 在tomcat上全手工部署Servlet3.0
  8. 【Linux】第一章 整合 JDK 和 MariaDB(附 Linux 基本命令)
  9. Paging Structures in the Different Paging Modes
  10. 数学建模 聚类模型
  11. 关于Android中使用Enum的一点总结
  12. Python根据拼音对中文排序
  13. w764位计算机右键管理,为win764位旗舰版右键添加“管理员取得所有权”方法
  14. 汇总:所有你该知道的AR/VR/3D技术与发展
  15. ffmpeg分离视频音频流
  16. 阿里云视频云正式支持AV1编码格式 为视频编码服务降本提效
  17. Myabtis-Plus(高级查询)
  18. 埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛L
  19. 你知道遥控器的通信原理吗?
  20. “笨办法”学Python3,Zed A. Shaw,习题15

热门文章

  1. linux下的Apache2 + mysql5 + php5 源码完整安装详解
  2. 在Java中如何从一个多层嵌套循环中直接跳出?
  3. 程序员的算法课(5)-动态规划算法
  4. JAVA六大线程池详解
  5. ajax then jquery,使用Jquery.ajax()。then()时无法.catch()错误
  6. vsc中HTML配置,vscode怎么配置node?
  7. 二维观察---曲线的裁剪
  8. ZEROC究竟是何方神圣? Leader-us 大神来的回答 Leader-us mycat的发起者
  9. 让你的微信私人账号也具备公众账号的 关键字回复功能
  10. iOS学习之NSBundle介绍和使用