在使用Ant Design Pro开发时,若是是组件渲染出错,生产环境下会直接致使整个页面白屏,形成了很是差的用户体验。通常来讲,当页面出错时,提示这个页面出错就好了,左边的菜单栏应该还要可以正常使用,这样的用户体验会好一些。html
可是组件渲染时因为不能在父组件使用try...catch捕获,所以一直是个比较难处理的问题。React 16引入了“错误边界(Error Boundaries)
”之后,如今能够优雅地处理这个问题,达到上面说的效果。react
经过错误边界处理之后,渲染组件错误后的效果图:
git
根据官网介绍:github
“错误边界是一种 React 组件,这种组件能够捕获并打印发生在其子组件树任何位置的 JavaScript 错误,而且,它会渲染出备用 UI,而不是渲染那些崩溃了的子组件树。错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。”antd
这个听起来有点拗口,简单说,只要在组件中定义static getDerivedStateFromError()
或componentDidCatch()
,这个组件就是一个错误边界。当它的子组件出错时,这个组件能够感知到而后根据实际状况处理,防止整个组件树直接崩溃。函数
static getDerivedStateFromError()
的使用场景是渲染备用UI(就是本文的应用场景)
componentDidCatch()
的使用场景是打印/记录错误信息(好比发送到sentry等BUG记录工具,本文没用到)工具
下面咱们结合具体的代码,给Antd Pro的基础排版组件src/layouts/BasicLayout.jsx
增长错误边界的处理,当具体页面出现错误时,提示用户出错,左边菜单还能继续使用。
咱们使用的版本是Ant Design Pro v4,BasicLayout.jsx已经使用函数式组件实现,可是边界处理目还不能经过React Hook去作,所以仍是要先改回类组件的实现方式。代码以下:测试
// src/layouts/BasicLayout.jsx // ...省略无关代码,改回类组件 class BasicLayout extends React.Component { componentDidMount() { const { dispatch } = this.props; if (dispatch) { dispatch({ type: 'user/fetchCurrent', }); } } handleMenuCollapse = payload => { const { dispatch } = this.props; if (dispatch) { dispatch({ type: 'global/changeLayoutCollapsed', payload, }); } }; // get children authority render () { const { dispatch, children, settings, location = { pathname: '/', }, } = this.props; const props = this.props; const authorized = getAuthorityFromRouter(props.route.routes, location.pathname || '/') || { authority: undefined, }; return (<> <ProLayout logo={logo} menuHeaderRender={(logoDom, titleDom) => ( <Link to="/"> {logoDom} {titleDom} </Link> )} onCollapse={this.handleMenuCollapse} menuItemRender={(menuItemProps, defaultDom) => { if (menuItemProps.isUrl || menuItemProps.children) { return defaultDom; } return <Link to={menuItemProps.path}>{defaultDom}</Link>; }} breadcrumbRender={(routers = []) => [ { path: '/', breadcrumbName: formatMessage({ id: 'menu.home', defaultMessage: 'Home', }), }, ...routers, ]} itemRender={(route, params, routes, paths) => { const first = routes.indexOf(route) === 0; return first ? ( <Link to={paths.join('/')}>{route.breadcrumbName}</Link> ) : ( <span>{route.breadcrumbName}</span> ); }} footerRender={footerRender} menuDataRender={menuDataRender} formatMessage={formatMessage} rightContentRender={rightProps => <RightContent {...rightProps} />} {...props} {...settings} > <Authorized authority={authorized.authority} noMatch={noMatch}> {children} </Authorized> </ProLayout> <SettingDrawer settings={settings} onSettingChange={config => dispatch({ type: 'settings/changeSetting', payload: config, }) } /> </> ); } };
而后在这个代码基础上,增长错误边界的处理代码:fetch
class BasicLayout extends React.Component { constructor(props) { super(props); // 默认没有错误 this.state = { hasError: false }; } // 增长错误边界代码,当发生错误时,state中的hasError会变成true static getDerivedStateFromError() { return { hasError: true }; } render () { const { hasError } = this.state; return (<> {/* 省略无关代码 */} <ProLayout > <Authorized authority={authorized.authority} noMatch={noMatch}> {/* 出现错误的时候,渲染错误提示组件 */} {hasError ? <Result status="error" title="程序发生错误,请反馈给服务提供商" /> : children} </Authorized> </ProLayout> <>) } }
完整的代码文件点击这里下载this
https://raw.githubusercontent.com/pheye/shopify-admin/master/src/layouts/BasicLayout.jsx
咱们以官方提供的示例工程为例,直接让分析页面出错,在render()
里面,加一句throw new Error('渲染出错');
,就能看到渲染出错时的效果了。
须要注意:错误边界仅能够捕获其子组件的错误,没法捕获自身的错误。
所以src/layouts/BasicLayout.js
中的错误边界,能够确保页面出错时左边的菜单栏仍是正常工做,可是若是是BasicLayout.js
自己的侧边栏或者头部出错也同样会白屏。src/layouts
的其余文件没有加错误边界,出错也还会白屏。要解决这个问题,能够对根组件作一层组装,增长边界处理,给用户更友好的提示。
可是对根组件的处理仍是替代不了单独对BasicLayout.js
增长边界处理,由于咱们但愿出错之后菜单栏还要可以正常使用。
最好的错误边界处理策略是根组件提供一个统一的错误处理,不一样的排版组件提示根据排版提供更友好的错误处理。