欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Ant Design Pro生产环境出错白屏的处理方法

程序员文章站 2022-03-26 12:13:51
背景 在使用Ant Design Pro开发时,如果是组件渲染出错,生产环境下会直接导致整个页面白屏,造成了非常差的用户体验。一般来说,当页面出错时,提示这个页面出错就行了,左边的菜单栏应该还要能够正常使用,这样的用户体验会好一些。 但是组件渲染时由于不能在父组件使用try...catch捕获,因此 ......

背景

在使用ant design pro开发时,如果是组件渲染出错,生产环境下会直接导致整个页面白屏,造成了非常差的用户体验。一般来说,当页面出错时,提示这个页面出错就行了,左边的菜单栏应该还要能够正常使用,这样的用户体验会好一些。

但是组件渲染时由于不能在父组件使用try...catch捕获,因此一直是个比较难处理的问题。react 16引入了“错误边界(error boundaries)”以后,现在可以优雅地处理这个问题,达到上面说的效果。

通过错误边界处理以后,渲染组件错误后的效果图:
Ant Design Pro生产环境出错白屏的处理方法

错误边界简介

根据官网介绍:

“错误边界是一种 react 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 javascript 错误,并且,它会渲染出备用 ui,而不是渲染那些崩溃了的子组件树。错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。”

这个听起来有点拗口,简单说,只要在组件中定义static getderivedstatefromerror()componentdidcatch(),这个组件就是一个错误边界。当它的子组件出错时,这个组件可以感知到然后根据实际情况处理,防止整个组件树直接崩溃。

static getderivedstatefromerror()的使用场景是渲染备用ui(就是本文的应用场景)
componentdidcatch()的使用场景是打印/记录错误信息(比如发送到sentry等bug记录工具,本文没用到)

antd解决白屏的实现方法

下面我们结合具体的代码,给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,
          })
        }
      />
    </>
    );
  }
};

然后在这个代码基础上,增加错误边界的处理代码:

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>
    <>)
  }
}

完整的代码文件点击这里下载

https://raw.githubusercontent.com/pheye/shopify-admin/master/src/layouts/basiclayout.jsx

测试

我们以官方提供的示例工程为例,直接让分析页面出错,在render()里面,加一句throw new error('渲染出错');,就能看到渲染出错时的效果了。
Ant Design Pro生产环境出错白屏的处理方法
Ant Design Pro生产环境出错白屏的处理方法

后记

需要注意:错误边界仅可以捕获其子组件的错误,无法捕获自身的错误。
因此src/layouts/basiclayout.js中的错误边界,可以确保页面出错时左边的菜单栏还是正常工作,但是如果是basiclayout.js本身的侧边栏或者头部出错也一样会白屏。src/layouts的其他文件没有加错误边界,出错也还会白屏。要解决这个问题,可以对根组件做一层组装,增加边界处理,给用户更友好的提示。
但是对根组件的处理还是替代不了单独对basiclayout.js增加边界处理,因为我们希望出错以后菜单栏还要能够正常使用。
最好的错误边界处理策略是根组件提供一个统一的错误处理,不同的排版组件提示根据排版提供更友好的错误处理。

参考文档

react错误边界
antd pro根组件