zoukankan      html  css  js  c++  java
  • 前端异常捕获方法汇总

    前端异常捕获,技术大纲

    1. 可疑区域增加 try...catch
    2. 全局监控JS异常: window.onerror
    3. 全局监控静态资源异常: window.addEventListener
    4. 全局捕获没有 catch 的 promise 异常:unhandledrejection
    5. iframe 异常:window.error
    6. VUE errorHandler 和 React componentDidCatch
    7. 监控网页崩溃:window 对象的 load 和 beforeunload
    8. Script Error跨域 crossOrigin 解决

    try...catch 的误区

    try...catch只能捕获到同步的运行时错误,对于语法和异步错误无能为力,捕获不到。

    1.同步运行时错误

    try {
    let name = 'Jack';
    console.log(nam);
    } catch(e) {
    console.log('捕获到异常:',e);
    }

    输出:

    捕获到异常: ReferenceError: nam is not defined
    at <anonymous>:3:15

    2.不能捕获语法错误,我们修改一个代码,删掉一个单引号

    try {
    let name = 'Jack;
    console.log(nam);
    } catch(e) {
    console.log('捕获到异常:',e);
    }

    输出:

    Uncaught SyntaxError: Invalid or unexpected token

    语法错误SyntaxError,不管是window.error还是try...catch都没法捕获异常。但是不用担心,在你写好代码按下保存那一刻,编译器会帮你检查是否有语法错误,如果有错误有会有个很明显的红红的波浪线,把鼠标移上去就能看到报错信息。因此,面对SyntaxError语法错误,一定要小心小心再小心

    3.异步错误

    try {
    setTimeout(() => {
    undefined.map(v => v);
    }, 1000)
    } catch(e) {
    console.log('捕获到异常:',e);
    }

    输出:

    Uncaught TypeError: Cannot read property 'map' of undefined
    at setTimeout (<anonymous>:3:11)

    可以看到,并没有捕获到异常。

    window.onerror 不是万能的

    当JS运行时错误发生时,window 会触发一个 ErrorEvent 接口的 error 事件,并执行 window.onerror() 。

     

    1.同步运行时错误

    window.onerror = function(message, source, lineno, colno, error) {
    // message:错误信息(字符串)。
    // source:发生错误的脚本URL(字符串)
    // lineno:发生错误的行号(数字)
    // colno:发生错误的列号(数字)
    // error:Error对象(对象)
    console.log('捕获到异常:',{message, source, lineno, colno, error});
    }
    UndefVar;

     

    2.语法错误

    window.onerror = function(message, source, lineno, colno, error) {
    console.log('捕获到异常:',{message, source, lineno, colno, error});
    }
    let name = 'Jack; // 少个单引号

    控制台打印出了这样的异常:

    Uncaught SyntaxError: Invalid or unexpected token

    可以看出,并没有捕获到异常。

    3.异步运行时错误

    window.onerror = function(message, source, lineno, colno, error) {
    console.log('捕获到异常:',{message, source, lineno, colno, error});
    }
    setTimeout(() => {
    UndefVar;
    });

    同样看到,我们捕获了异常:


     

    4.网络请求的异常

    <script>
    window.onerror = function(message, source, lineno, colno, error) {
    console.log('捕获到异常:',{message, source, lineno, colno, error});
    return true;
    }
    </script>
    <img src="./xxx.png">

    我们发现,不论是静态资源异常,或者接口异常,错误都无法捕获到。

    注意:

    window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出(浏览器接收后报红),否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxxx

    window.onerror 最好写在所有JS脚本的前面,否则有可能捕获不到错误

    window.onerror无法捕获语法错误

    捕获静态资源加载错误

    window.addEventListener

    当一项资源(如图片和脚本加载失败),加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror处理函数。这些error事件不会向上冒泡到window, 不过(至少在 Chrome 中)能被单一的window.addEventListener 捕获。

    <script>
    window.addEventListener('error', (error) => {
    console.log('捕获到异常:', error);
    }, true)
    </script>
    <img src="./xxxx.png">

    由于网络请求异常不会事件冒泡,因此必须在捕获阶段将其捕捉到才行,但是这种方式虽然可以捕捉到网络请求的异常,但是无法判断 HTTP 的状态是 404 还是其他比如 500 等等,所以还需要配合服务端日志才进行排查分析才可以。

    注意:

    不同浏览器下返回的 error 对象可能不同,需要注意兼容处理。 需要注意避免 window.addEventListener 重复监听。

    到此为止,我们学到了:在开发的过程中,对于容易出错的地方,可以使用try{}catch(){}来进行错误的捕获,做好兜底处理,避免页面挂掉。而对于全局的错误捕获,在现代浏览器中,我倾向于只使用使用window.addEventListener('error'),window.addEventListener('unhandledrejection')就行了。如果需要考虑兼容性,需要加上window.onerror,三者同时使用,window.addEventListener('error')专门用来捕获资源加载错误。

    Promise Catch

    我们知道,在 promise 中使用 catch 可以非常方便的捕获到异步 error 。

    没有写catch的promise中抛出的错误无法被onerror或try...catch捕获到,所以务必在promise中写catch做异常处理。

    有没有一个全局捕获promise的异常呢?答案是有的。 Uncaught Promise Error就能做到全局监听,使用方式:

    window.addEventListener("unhandledrejection", function(e){
    // e.preventDefault(); // 阻止异常向上抛出
    console.log('捕获到异常:', e);
    });
    Promise.reject('promise error');

    所以,正如我们上面所说,为了防止有漏掉的 promise 异常,建议在全局增加一个对 unhandledrejection 的监听,用来全局监听 Uncaught Promise Error。

    iframe 异常

    对于 iframe 的异常捕获,我们还得借力 window.onerror:

    window.onerror = function(message, source, lineno, colno, error) {
    console.log('捕获到异常:',{message, source, lineno, colno, error});
    }

    下面一个简单的例子:

    <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});
    };
    </script>

    Script error

    在进行错误捕获的过程中,很多时候并不能拿到完整的错误信息,得到的仅仅是一个"Script Error"。

    产生原因

    当加载自不同域的脚本中发生语法错误时,为避免信息泄露,语法错误的细节将不会报告,而是使用简单的"Script error."代替。

    解决办法

    一般情况,如果出现 Script error 这样的错误,基本上可以确定是跨域问题。这时候,是不会有其他太多辅助信息的,但是解决思路无非如下:

    跨源资源共享机制( CORS ):我们为 script 标签添加 crossOrigin 属性。

    <script src="http://www.jshaman.com/test.js" crossorigin></script>

    崩溃和卡顿

    卡顿也就是网页暂时响应比较慢, JS可能无法及时执行。但崩溃就不一样了,网页都崩溃了,JS都不运行了,还有什么办法可以监控网页的崩溃,并将网页崩溃上报呢?

    1.利用 window 对象的 load 和 beforeunload 事件实现了网页崩溃的监控。 不错的文章,推荐阅读:Logging Information on Browser Crashes。

    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'));
    }

    2.基于以下原因,我们可以使用 Service Worker 来实现网页崩溃的监控:

    • Service Worker 有自己独立的工作线程,与网页区分开,网页崩溃了,Service Worker一般情况下不会崩溃
    • Service Worker 生命周期一般要比网页还要长,可以用来监控网页的状态
    • 网页可以通过 navigator.serviceWorker.controller.postMessage API 向掌管自己的 SW 发送消息

    VUE errorHandler

    在Vue中,异常可能被Vue自身给try...catch了,不会传到window.onerror事件触发。不过不用担心,Vue提供了特有的异常捕获,比如Vux2.x中我们可以这样用:

    Vue.config.errorHandler = function (err, vm, info) {
    let {
    message, // 异常信息
    name, // 异常名称
    script, // 异常脚本url
    line, // 异常行号
    column, // 异常列号
    stack // 异常堆栈信息
    } = err;

    // vm为抛出异常的 Vue 实例
    // info为 Vue 特定的错误信息,比如错误所在的生命周期钩子
    }

    React 异常捕获

    在React,可以使用ErrorBoundary组件包括业务组件的方式进行异常捕获,配合React 16.0+新出的componentDidCatch API,可以实现统一的异常捕获和日志上报。

    我们来举一个小例子,在下面这个 componentDIdCatch(error,info) 里的类会变成一个 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;
    }
    }

    componentDidCatch() 方法像JS的 catch{} 模块一样工作,但是对于组件,只有 class 类型的组件(class component )可以成为一个 error boundaries 。

    实际上,大多数情况下我们可以在整个程序中定义一个 error boundary 组件,之后就可以一直使用它了!

    需要注意的是:error boundaries并不会捕捉下面这些错误:

    事件处理器 异步代码 服务端的渲染代码 在 error boundaries 区域内的错误

    有些情况下,不想让人发现自己的捕获代码,这时,可以用JShaman之类平台进行代码混淆加密,使代码成为不可识别的乱码。

  • 相关阅读:
    将不确定变为确定~接口应该是什么
    架构,改善程序复用性的设计~目录(附核心原代码)
    php实战第二十五天
    java一道简单的括号匹配问题
    屈原坐上神十带来了iOS 7
    《别独自用餐》 警句摘录
    php实战第二十四天
    偷了世界的程序员
    变故
    “快排”笔记
  • 原文地址:https://www.cnblogs.com/liuhao-web/p/13184486.html
Copyright © 2011-2022 走看看