react子组件报错导致的线上页面空白

457 阅读3分钟

1.背景:react子组件报错页面 Game Over

在react开发时,大多都遇到过子组件种一个按钮点击触发逻辑导致整个页面空白了,此时心里一万个 “不可能” “不是我的bug” “我都是经过测试检察官们拍板的”。不对不对,我已经接入了监控做了全局的error异常处理甚至加了try catch,咋还有事故呢?

But… 小流量时还能说的过去,突然上千万上亿的流量蜂拥而来时,我慌的一笔啊,开始怀疑我的代码人生,职业生涯上要来个p0级别的事故了,我在某大厂的职业生涯要面壁思过了…

2.冷静分析,开始查阅官方文档

你的error监控仅仅是监控,不是代码的强壮。你的兜底方案呢,降级处理呢?我放池塘养鱼了…😭

稍加思索.png

3.还好还好,终于看到官方解释

React16 起,任何未被错误边界捕获的错误将会导致整个 React 组件树被卸载,从而出现白屏问题。**

开始解决:

Error Boundaries

A JavaScript error in a part of the UI shouldn’t break the whole app. To solve this problem for React users, React 16 introduces a new concept of an “error boundary”.

官方文档:reactjs.org/docs/error-…

以上所说的即是—错误边界

那么什么是错误边界?

即默认情况下,若一个组件在渲染期间(render) 发生错误,会导致整个组件树全部被卸载。

错误边界是一种 React 组件,这种组件可以捕获发生在其子组件树任何位置的 JavaScript 错误,并打印这些错误,同时展示降级 UI,而并不会渲染那些发生崩溃的子组件树。错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误

错误边界的作用范围

虽然函数式组件无法定义 Error Boundary,但 Error Boundary 可以捕获函数式组件的异常错误。

4.解决方案

React中提供了两个与错误处理相关的api:

getderivedstatefromerror:静态方法,当错误发生后,提供一个机会渲染 Fallback UI。

componentDidCatch:组件实例方法,当错误发生后,提供一个机会记录错误信息。

如果一个 class 组件中定义了 getDerivedStateFromError() 或 componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界。当抛出错误后,请使用 getDerivedStateFromError() 渲染备用 UI ,使用 componentDidCatch() 打印错误信息。

对于我这个ctrl c/v的搬砖队长来说信手拈来:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    // 这里捕获到后即对子组件进行兜底方案
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // You can also log the error to an error reporting service
    // 这里是前端监控的sgm log上报
    logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

使用方法:

<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

官方示例代码:codepen.io/gaearon/pen…

推荐在getDerivedStateFromError() 中处理 fallback UI,而不是在 componentDidCatch() 方法中,componentDidCatch() 在未来的 React 版本中可能会被废弃,仅供参考,以上方案只是最简单的方式,深层次的还需要大家自己的加工处理。

5.扩展

当然上述是子组件报错的问题,那么如何解决深层嵌套的字段错误导致的异常呢?

多数写法是

let data = rs && rs.data && rs.data.list

可选链操作符?.


let data = rs?.data?.list 以上两个对比是等价的

是不是美观度上升了一个台阶,nice。

当然对于页面首屏加载的快慢优化会在前端骨架屏对比中详细去介绍下: ssr非侵入式骨架屏和传统骨架屏的pk, 下期再见。