详解JS错误处理:前端JS/Vue/React/Iframe/跨域/Node

js毛病捕捉

js毛病的本质,也是发出一个事宜,处置惩罚他

error实例对象

  • 对象属性

    • message:毛病提醒信息
    • name:毛病称号(非标准属性)宿主环境给予
    • stack:毛病的客栈(非标准属性)宿主环境给予
  • 对象范例(7种)

    • SyntaxError对象是剖析代码时发作的语法毛病
    • ReferenceError对象是援用一个不存在的变量时发作的毛病
    • RangeError对象是一个值超越有用局限时发作的毛病(一是数组长度为负数,二是Number对象的要领参数超越局限,以及函数客栈凌驾最大值)
    • TypeError对象是变量或参数不是预期范例时发作的毛病:对字符串、布尔值、数值等原始范例的值运用new敕令
    • URIError对象是 URI 相干函数的参数不准确时抛出的毛病:运用函数不当
    • eval函数没有被准确实行时,会抛出EvalError毛病 – 不再运用,为了代码兼容
  • 自定义毛病

    function UserError(message) {
      this.message = message || '默许信息';
      this.name = 'UserError';
    }
    
    UserError.prototype = new Error();
    UserError.prototype.constructor = UserError;
    new UserError('这是自定义的毛病!');

Js运行时毛病处置惩罚机制

  • try..catch…finally

    • 局限:用来捕捉任何范例的同步毛病,能够捕捉async / await的代码,但没法捕捉promise、setTimeout、dom回调(eg:onclick点击回调)的代码,在回调函数内里写try…catch能够捕捉,但包在表面不会捕捉,没法捕捉语法毛病
    • 异步不捕捉缘由:
    • async/await捕捉缘由:
  • window.onerror

    • 局限:同步毛病和异步毛病都能够捕捉,但没法捕捉到静态资本非常,或许接口非常(收集要求非常不会事宜冒泡,因而必须在捕捉阶段将其捕捉到才行),没法捕捉语法毛病
    • 道理:当 JS 运行时毛病发作时,window 会触发一个 ErrorEvent 接口的 error 事宜
    • 参数

      /**
      * @param {String}  message    毛病信息
      * @param {String}  source    失足文件
      * @param {Number}  lineno    行号
      * @param {Number}  colno    列号
*/
window.onerror = function(message, source, lineno, colno, error) {
   console.log('捕捉到非常:',{message, source, lineno, colno, error});
}
```
  • 补充:window.onerror 函数只要在返回 true 的时刻,非常才不会向上抛出,不然即使是晓得非常的发作控制台照样会显现 Uncaught Error: xxxxx
  • onerror 最好写在一切 JS 剧本的前面,不然有能够捕捉不到毛病;(捕捉的是全局毛病)

资本加载毛病

  • window.addEventListener(一项资本(如图片或剧本)加载失利,加载资本的元素会触发一个 Event 接口的 error 事宜,并实行该元素上的onerror() 处置惩罚函数,有浏览器兼容题目)
  • 注重:只能捕捉没法冒泡

    window.addEventListener('error', (error) => {
        console.log('捕捉到非常:', error);
    }, true) // 一定要加true,捕捉但不冒泡
  • Script error跨域的静态资本加载非常捕捉(cdn文件等)

    跨域文件只会报Script error,没有细致信息,怎样处理:

    • 客户端:script标签增添crossOrigin
    • 效劳端:设置:Access-Control-Allow-Origin
    <script src="http://jartto.wang/main.js" crossorigin></script>

iframe非常

  • 运用window.onerror

    <iframe src="./iframe.html" frameborder="0"></iframe>
    <script>
      window.frames[0].onerror = function (message, source, lineno, colno, error) {
        console.log('捕捉到 iframe 非常:',{message, source, lineno, colno, error});
        return true;
      };
    </script>

promise非常捕捉

  • 没有写 catchPromise 中抛出的毛病没法被 onerrortry-catch 捕捉到
  • 为了防备有遗漏的 Promise 非常,发起在全局增添一个对 unhandledrejection 的监听,用来全局监听Uncaught Promise Error

    window.addEventListener("unhandledrejection", function(e){
      console.log(e);
    });
  • 补充:假如去掉控制台的非常显现,须要加上:event.preventDefault();

vue非常捕捉

  • VUE errorHandler

    Vue.config.errorHandler = (err, vm, info) => {
      console.error('经由过程vue errorHandler捕捉的毛病');
      console.error(err);
      console.error(vm);
      console.error(info);
    }

React非常捕捉

  • componentDidCatch(React16)
  • 新概念Error boundary(React16):UI的某部份激发的 JS 毛病不会损坏全部顺序(只要 class component能够成为一个 error boundaries)

    不会捕捉以下毛病

    1.事宜处置惩罚器
    2.异步代码
    3.效劳端的衬着代码
    4.在 error boundaries 地区内的毛病
  • Eg: 全局一个error boundary 组件就够用啦

    class ErrorBoundary extends React.Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }
     
      componentDidCatch(error, info) {
        // Display fallback UI
        this.setState({ hasError: true });
        // You can also log the error to an error reporting service
        logErrorToMyService(error, info);
      }
     
      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>

页面崩溃和卡顿处置惩罚

  • 卡顿

    • 网页临时相应比较慢, JS 能够没法及时实行
    • 处理:window 对象的 loadbeforeunload 事宜完成了网页崩溃的监控

      window.addEventListener('load', function () {
          sessionStorage.setItem('good_exit', 'pending');
          setInterval(function () {
              sessionStorage.setItem('time_before_crash', new Date().toString());
          }, 1000);
        });
        window.addEventListener('beforeunload', function () {
          sessionStorage.setItem('good_exit', 'true');
        });
        if(sessionStorage.getItem('good_exit') &&
          sessionStorage.getItem('good_exit') !== 'true') {
          /*
              insert crash logging code here
          */
          alert('Hey, welcome back from your crash, looks like you crashed on: ' + sessionStorage.getItem('time_before_crash'));
        }
  • 崩溃

    • JS 都不运行了,另有什么方法能够监控网页的崩溃,并将网页崩溃上报呢
    • 处理:Service Worker 来完成网页崩溃的监控

      • Service Worker 有自身自力的事情线程,与网页区离开,网页崩溃了,Service Worker平常情况下不会崩溃;
      • Service Worker 生命周期平常要比网页还要长,能够用来监控网页的状况;
      • 网页能够经由过程 navigator.serviceWorker.controller.postMessage API 向掌管自身的 SW发送音讯。

毛病上报机制

  • Ajax 发送数据
    因为 Ajax 要求自身也有能够会发作非常,而且有能够会激发跨域题目,平常情况下更引荐运用动态建立 img 标签的情势举行上报。
  • 动态建立 img 标签的情势 更经常使用,简朴,无逾越题目
function report(error) {
  let reportUrl = 'http://jartto.wang/report';
  new Image().src = `${reportUrl}?logs=${error}`;
}
  • 假如你的网站访问量很大,那末一个必定的毛病发送的信息就有很多条,这时刻,我们须要设置收集率,从而减缓效劳器的压力
Reporter.send = function(data) {
  // 只收集 30%
  if(Math.random() < 0.3) {
    send(data)      // 上报毛病信息
  }
}

js源代码紧缩怎样定位:成熟计划供应sentry

sentry 是一个及时的毛病日记追踪和聚合平台,包含了上面 sourcemap 计划,并支撑更多功用,如:毛病挪用栈,log 信息,issue治理,多项目,多用户,供应多种语言客户端等,

这里不过量叙说,以后在搭建sentry效劳时,会再补篇博文

补充:node效劳端毛病处置惩罚机制

全栈开辟,后端采纳express库,在这里补充一下,node效劳的毛病处置惩罚计划

  • 毛病分类

    • 平常毛病处置惩罚:如某种回退,基本上只是说:“有毛病,请再试一次或联络我们”。这并非迥殊智慧,但至少关照用户,有处所错了——而不是无穷加载或举行相似地处置惩罚
    • 特别毛病处置惩罚为用户供应细致信息,让用户相识有什么题目以及怎样处理它,比方,有信息丧失,数据库中的条目已存在等等
  • 步骤

    • 1. 构建一个自定义 Error 组织函数:让我们方便地取得客栈跟踪

      class CustomError extends Error {
          constructor(code = 'GENERIC', status = 500, ...params) {
              super(...params)
              if (Error.captureStackTrace) {
                  Error.captureStackTrace(this, CustomError)
              }
              this.code = code
              this.status = status
          }
      }
      
      module.exports = CustomError
    • 2.处置惩罚路由:关于每个路由,我们要有雷同的毛病处置惩罚行动

      wT:在默许情况下,因为路由都是封装的,所以 Express 并不真正支撑那种体式格局

      处理:完成一个路由处置惩罚顺序,并把现实的路由逻辑定义为一般的函数。如许,假如路由功用(或任何内部函数)抛出一个毛病,它将返回到路由处置惩罚顺序,然后能够传给前端

      const express = require('express')
      const router = express.Router()
      const CustomError = require('../CustomError')
      
      router.use(async (req, res) => {
          try {
              const route = require(`.${req.path}`)[req.method]
      
              try {
                  const result = route(req) // We pass the request to the route function
                  res.send(result) // We just send to the client what we get returned from the route function
              } catch (err) {
                  /*
                  This will be entered, if an error occurs inside the route function.
                  */
                  if (err instanceof CustomError) {
                      /* 
                      In case the error has already been handled, we just transform the error 
                      to our return object.
                      */
      
                      return res.status(err.status).send({
                          error: err.code,
                          description: err.message,
                      })
                  } else {
                      console.error(err) // For debugging reasons
      
                      // It would be an unhandled error, here we can just return our generic error object.
                      return res.status(500).send({
                          error: 'GENERIC',
                          description: 'Something went wrong. Please try again or contact support.',
                      })
                  }
              }
          } catch (err) {
              /* 
              This will be entered, if the require fails, meaning there is either 
              no file with the name of the request path or no exported function 
              with the given request method.
              */
              res.status(404).send({
                  error: 'NOT_FOUND',
                  description: 'The resource you tried to access does not exist.',
              })
          }
      })
      
      module.exports = router
      
      // 现实路由文件
      const CustomError = require('../CustomError')
      
      const GET = req => {
          // example for success
          return { name: 'Rio de Janeiro' }
      }
      
      const POST = req => {
          // example for unhandled error
          throw new Error('Some unexpected error, may also be thrown by a library or the runtime.')
      }
      
      const DELETE = req => {
          // example for handled error
          throw new CustomError('CITY_NOT_FOUND', 404, 'The city you are trying to delete could not be found.')
      }
      
      const PATCH = req => {
          // example for catching errors and using a CustomError
          try {
              // something bad happens here
              throw new Error('Some internal error')
          } catch (err) {
              console.error(err) // decide what you want to do here
      
              throw new CustomError(
                  'CITY_NOT_EDITABLE',
                  400,
                  'The city you are trying to edit is not editable.'
              )
          }
      }
      
      module.exports = {
          GET,
          POST,
          DELETE,
          PATCH,
      }
    • 3.构建全局毛病处置惩罚机制

      process.on('uncaughtException', (error: any) => {
          logger.error('uncaughtException', error)
      })
      
      process.on('unhandledRejection', (error: any) => {
          logger.error('unhandledRejection', error)
      })

总结:

1.可疑地区增添 Try-Catch
2.全局监控 JS 非常 window.onerror
3.全局监控静态资本非常 window.addEventListener
4.捕捉没有 CatchPromise 非常:unhandledrejection
5.VUE errorHandlerReact componentDidCatch
6.监控网页崩溃:window 对象的 loadbeforeunload
7.跨域 crossOrigin 处理

援用

    原文作者:Rainie
    原文地址: https://segmentfault.com/a/1190000019226851
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞