宇宙最全的js错误监控系统(下)

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

前言

我们回顾一下上一篇讲到的内容:window.onerror、iframe中的error、promise的error以及Script error;今天我们继续补充剩下的内容

try catch

try catch只能捕捉到同步代码,对于异步代码中的错误无能为力

  try {
      setTimeout(()=>{
        const obj = null
        console.log(obj.name)
      })
  } catch (error) {
    console.log(error)
  }
复制代码

这段代码我们是无法拿到其错误信息的,我们需要去掉try catch换成window.onerror来捕捉它

try catch语句有一个副作用:由于catch中暴露了一个error对象,导致作用域链被延长,GC可能无法及时回收整个error对象,即使catch中没有使用,所以try catch语句我们也是尽量在需要的时候再去使用,不要写无意义的try catch,或者能够使用其他方式代替的就不要用try catch

window.addEventListener('error')

当有资源加载错误时,我们可以使用相应标签的事件进行处理,例如:img的onerror事件,script的onerror事件等

img.onerror = ()=>{
    alert('图片加载失败')
}
复制代码

但是实际项目中我们一般都没有写这个onerror事件,当想要去捕捉的时候又需要一个一个地添加上去,有没有一个一劳永逸的办法呢?

那就是window.addEventListener('error'),从经验来看它应该和window.onerror一样啊,像scroll事件,我们用addEventListener一般就不需要on,而直接在window对象上设置的话又需要on

window.onscroll = handleScroll
window.addEventListener("scroll",handleScroll)
复制代码

但是在这里window.addEventListener('error')不等于window.onerror,我们看一下下面的示例:

   function loadScript () {
    const i = document.createElement('script')
    i.onload = () => console.log('loaded')
    // i.onerror = () => console.log('load failed')
    i.src = '404.webp'
    document.body.append(i)
  }

  function addErrorEvent () {
    window.addEventListener('error', (e) => {
      console.log('e', e)
    }, true)

    window.onerror = (...args) => {
      console.log('win onerror', args)
    }
  }

  addErrorEvent()
  loadScript()
  abc()
复制代码

image.png

  1. window.addEventListener('error')可以捕捉资源加载错误,而window.onerror只能捕捉js错误
  2. window.addEventListener可以绑定多个事件处理函数
  3. 参数不同:onerror捕捉到的有【代码行数,错误堆栈】等信息,而window.addEventListener('error')它回调函数的参数是【Event对象】,可以得到发生错误的dom,但是event对象中包含代码行数和error对象,所以后者对于前者是一个包含关系

Vue提供的error钩子

Vue中提供了一个捕捉错误的方法errorHandler/errorCaptured

全局Vue错误捕捉:errorHandler

let vuePlugin = function errorPlugin(error, _Vue) {
  if (!_Vue || !_Vue.config) {
    return
  }

  let _oldOnError = _Vue.config.errHandler
  _Vue.config.errHandler = function errHandler(errMsg, vm, info) {
    console.log(errMsg)

// 使用这个配置之后控制台的错误会被清空,为了保证既能够捕捉错误又不影响控制台输出,所以使用console.error来进行输出
    if (typeof console !== 'undefined' && typeof console.error === 'function') {
      console.error(errMsg)
    }

    if (typeof _oldOnError === 'function') {
      _oldOnError.call(this, errMsg, vm, info)
    }
  }
}
复制代码

errorCaptured:钩子函数,捕捉子孙组件中的错误

类型(err: Error, vm: Component, info: string) => ?boolean

类似于React错误边界,返回 false 以阻止错误继续向上传播。我们可以在报错之后给页面设置错误提醒页面。

render错误钩子【只能在开发环境使用】:renderError

当render函数渲染遇到错误时提供另外一种渲染输出;相当于render渲染正常输出,而renderError渲染错误输出,但是很遗憾只能在开发环境使用,对我们来说帮助不大

React中的error钩子

React中有一个概念:错误边界,在16之后逐渐确定下来,可以使用static getDerivedStateFromError() 或 componentDidCatch()形成错误边界兜底,他们的区别在于一个是静态方法无法访问实例方法,而componentDidCatch可以访问,一般情况下我们都使用它来创建错误边界

React错误边界可以局部创建也可以全局创建

  1. 局部构建可以使用HOC
const ErrorBoundary = errorInfo => WrapComponent =>{
    return class ErrorBoundary extends React.Component{
        constructor(props) {
            super(props);
            this.state = { hasError: false };
        }
        
        componentDidCatch(error, info) {
            this.setState({hasError: true});
        }
        
        render() {
            if (this.state.hasError) {
                return <h1>页面出错啦,错误提示:{errorInfo}</h1>;
            }
            return <WrapComponent/>;
        }
    }
}

@ErrorBoundary("Businss组件出错")
class Business extends Component{

}
复制代码
  1. 组件最外层包裹一层ErrorBoundary
class ErrorBoundary extends Componet{
  componentDidCatch(error,info){
    console.log(error,info)
  }

  render(){
   return  this.props.children
  }
}

<ErrorBoundary>
    <Layout/>
</ErrorBoundary>
复制代码

nodejs中的报错

  • 标准的 JavaScript 错误,例如EvalError、SyntaxError、RangeError、ReferenceError、TypeError和 URIError
  • 由底层操作系统约束触发的系统错误,例如尝试打开不存在的文件或尝试通过关闭的套接字发送数据。
  • 由应用程序代码触发的用户指定的错误。
  • AssertionError 是特殊的错误类,当 Node.js 检测到不应该发生的异常逻辑违规时会触发。 这些通常由 node:assert 模块引发。

uncaughtException

当未捕获的 JavaScript 异常一直冒泡回到事件循环时,则会触发 'uncaughtException' 事件。发生该错误时进程将以0退出。

process.on("uncaughtException",(error,origin)=>{
    // origin用来甄别是源自”promise错误“还是“同步错误”
       console.log(process.stderr.fd,
    `报错信息为: ${err}\n` +
    `发生错误的根源: ${origin}`
    )
})
复制代码

unhandledRejection

每当 Promise 被拒绝并且在事件循环的一个轮询内没有错误句柄附加到承诺时,则会触发 'unhandledRejection' 事件。

process.on('unhandledRejection', (reason, promise) => {
  console.log('Unhandled Rejection at:', promise, 'reason:', reason);
});
复制代码

总结

至此js错误监控就已经完结;我们重新回顾一下:try catch捕捉同步错误、window.addEventListener('error')捕捉js错误和资源错误、Vue中使用errorCaptured捕捉局部错误,errorHandler捕捉全局错误、React拥有getDerivedStateFromError和 componentDidCatch两个钩子函数;nodejs中可监听uncaughtException,unhandledRejection事件来记录错误信息

猜你喜欢

转载自juejin.im/post/7105300743455768612