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

更新日期: 2019-05-19阅读: 2.2k标签: 错误

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异常捕获

没有写 catch 的 Promise 中抛出的错误无法被 onerror 或 try-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 对象的 load 和 beforeunload 事件实现了网页崩溃的监控

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.捕获没有 Catch 的 Promise 异常:unhandledrejection
5.VUE errorHandler 和 React componentDidCatch
6.监控网页崩溃:window 对象的 load 和 beforeunload

7.跨域 crossOrigin 解决


原文来自:https://segmentfault.com/a/1190000019226851


链接: https://www.fly63.com/article/detial/3373

解决Cannot read property range of null 错误

vue工程npm run serve/start/dev启动时,node_modules文件报:Cannot read property range of null 错误,该问题是babel-eslint版本更新问题导致的;

HTTP 400 错误 - 请求无效 (Bad request)

在ajax请求后台数据时有时会报 HTTP 400 错误 - 请求无效 (Bad request);出现这个请求无效报错说明请求没有进入到后台服务里;原因:前端提交数据的字段名称或者是字段类型和后台的实体类不一致

js异步错误捕获

我们都知道 try catch 无法捕获 setTimeout 异步任务中的错误,那其中的原因是什么。以及异步代码在 js 中是特别常见的,我们该怎么做才比较?

不能执行已释放Script的代码

父页面初始化声明变量a为数组(数组对象是引用类型,赋值传递的是地址),创建iframe子页面后给父页面变量a赋值,赋值后销毁iframe子页面,再次调用变量a的时候就会抛出异常‘SCRIPT5011:不能执行已释放Script的代码’。

nodejs提示 cross-device link not permitted, rename 错误解决方法

文件上传的功能时候,调用fs.renameSync方法错误,这个提示是跨区重命名文件出现的权限问题。先从源文件拷贝到另外分区的目标文件,然后再unlink,就可以了。

Js中使用innerHTML的缺点是什么?

如果在JavaScript中使用innerHTML,缺点是:内容随处可见;不能像“追加到innerHTML”一样使用;innerHTML不提供验证,因此我们可能会在文档中插入有效的和破坏性的HTML并将其中断

Web前端开发,必须规避的8个错误点!

现在,有越来越多所谓的“教程”来帮助我们提高网站的易用性。我们收集了一些在Web开发中容易出错和被忽略的小问题,并且提供了参考的解决方案,以便于帮助Web开发者更好的完善网站。

web前端错误监控

为什么要做前端错误监控?1. 为了保证产品的质量2. 有些问题只存在于线上特定的环境3. 后端错误有监控,前端错误没有监控,前端错误分为两类: 即时运行错误和资源加载错误

自定义错误及扩展错误

当我们在进行开发的时候,通常需要属于我们自己的错误类来反映任务中可能出现的特殊情况。对于网络操作错误,我们需要 HttpError,对于数据库操作错误,我们需要 DbError,对于搜索操作错误,我们需要 NotFoundError,等等

Js中最常见的错误

最近查看了一些项目后,发现了几个最常见的JavaScript 错误。我们会告诉你什么原因导致了这些错误,以及如何防止这些错误发生。如果你能够避免落入这些 “陷阱”,你将会成为一个更好的开发者。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!