美文网首页
前端异常捕获

前端异常捕获

作者: 菜鸡前端 | 来源:发表于2021-09-02 14:24 被阅读0次

    1. JS Error 对象

    在 Error 对象中主要有3个属性

    • name 错误名称
    • message 错误描述
    • stack 错误的堆栈信息

    请注意浏览器兼容问题,IE/Firfox/Chrome 差异。

    2. JS 常见错误类型

    • ReferenceError(引用错误)比如访问未声明的变量
    • SyntaxError(语法错误)语法规则写错
    • RangeError(范围错误) 比如死循环 Uncaught RangeError: Maximum call stack size exceeded
    • 还有很多其他的错误类型

    3. JS 如何捕获异常

    3.1 常见的错误

    • 资源加载报错
    • JS 代码报错
      • promise.reject 错误
      • 非 promise.reject 错误

    3.2 资源加载异常 (xxx loader error)

    • 媒体资源加载
    • CSS/JS 脚本资源
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>资源不存在异常</title>
      <!-- main.css 不存在 -->
      <link rel="stylesheet" href="./main.css">
      <!-- main.js 不存在 -->
      <script src="./main.js" async></script>
    </head>
    
    <body>
      <!-- main.png 不存在 -->
      <img src="./main.png" alt="">
    </body>
    
    <script>
      // 捕获资源加载失败错误 js css img ...
      addEventListener('error', e => {
        const target = e.target
        console.log({
          type: target.localName,
          url: target.src || target.href,
          msg: (target.src || target.href) + ' load error',
          time: new Date().getTime(),
        })
      }, true) // 注意是在捕获阶段,否则无法捕获错误
    </script>
    
    </html>
    
    image.pngimage.png

    注意是在捕获阶段,否则无法捕获错误。

    3.3 JS 代码报错 (非Promise.reject)

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>JS异常(非Promise.reject)的捕获</title>
    </head>
    
    <body>
    </body>
    <script>
      window.addEventListener('error', e => {
        console.log(e.filename)
        console.log(e.message)
        console.log(e.error);
      })
    
      // 1. 可以捕获 ReferenceError: a is not defined
      // console.log(a) 
    
      // 2. 可以捕获 Error: throw new Error
      // throw new Error('throw new Error');
    
      // 3. 不能捕获, 需要使用 unhandledrejection 事件来捕获
      // Promise.reject('Promise.reject')
    </script>
    
    </html>
    

    3.4 JS 代码报错 (Promise.reject)

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>JS异常(Promise.reject)的捕获</title>
    </head>
    <body>
    </body>
    <script>
      /**
       * 不能获取 行号/列号/所属文件等信息
       */
      window.addEventListener('unhandledrejection', e => {
        console.log(e) // e 的 类型为 PromiseRejectionEvent
      })
      Promise.reject('this is a reject reason')
    </script>
    </html>
    
    image.pngimage.png

    4. chrome JS异常解析

    • unhandledrejection 事件异常类型,没有堆栈信息
    • 资源加载 异常类型,没有堆栈信息
    image.pngimage.png

    e.stack 第一行为错误描述,后面行为每一个 stack 的单独信息,sentry 中的 stacktrace 即通过解析 e.stack 而获得,解析逻辑为:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>chrome JS异常解析</title>
    </head>
    
    <body>
      <h1>hello sentry</h1>
      <button id="btn" onClick="doClick()">click</button>
    </body>
    <script src="https://browser.sentry-cdn.com/5.5.0/bundle.min.js" crossorigin="anonymous"></script>
    <script>
      // Sentry.init({ dsn: 'http://0a6466043b1548159093e55f0494c56f@localhost:9000/2' });
      function doClick() {
        try {
          console.log(a)
        } catch (e) {
          resolveStack(e)
        }
      }
    
      function resolveStack(e) {
        const stackList = []
        const list = e.stack.split('\n');
        list.forEach((element, index) => {
          if (index !== 0) {
            const [fun, stackMsg] = element.replace('    at ', '').split(' ')
            const [protol, filename, lineno, colno] = stackMsg.replace('(', '').replace(')', '').split(':');
            stackList.push({
              filename: protol + ':' + filename, // 文件名
              colno: colno, // 列数
              function: fun, // 调用函数
              lineno: lineno // 行数
            })
          }
        });
        console.log(stackList)
      }
    </script>
    </html>
    

    5. 捕获跨域JS资源异常

    跨域错误默认提示 Script error,而无法捕获到具体的错误信息:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    
    <body></body>
      
    <script>
      // 跨域错误默认提示 Script error
      // 真实错误为:Uncaught ReferenceError: a is not defined
      window.addEventListener('error', (e)=>{
        console.log('error', e.message) // Script error
      }, true);
    </script>
    <script src="http://demo.airtlab.com/lib/main.js"></script>
    </html>
    
    image.pngimage.png

    如何解决?客户端添加 crossorigin 属性,服务端添加 Access-Control-Allow-Origin: * 允许跨域:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    
    <body></body>
    <script>
      // 跨域错误默认提示 Script error
      window.addEventListener('error', (e)=>{
        console.log('error', e) // 
      }, true);
    </script>
    <script crossorigin="anonymous" src="http://demo.airtlab.com/lib/main.js"></script>
    </html>
    

    6. 本文总结

    对于异常捕获有几个关键点:

    • 资源加载异常的捕获,是在捕获阶段,否则无法捕获错误
    • 资源加载异常 和 Promise.reject(unhandledrejection) 异常 没有堆栈信息
    • 捕获跨域JS资源异常,需要特殊处理,客服端添加 crossorigin,服务端添加 CORS

    相关文章

      网友评论

          本文标题:前端异常捕获

          本文链接:https://www.haomeiwen.com/subject/tnzqwltx.html