react中的错误处理

栏目: IOS · Android · 发布时间: 4年前

内容简介:持续更新中对
react中的错误处理

持续更新中 react相关库源码浅析

解决的两个问题

  • 收集错误
  • 自定义事件实现 try...catch 错误捕获功能同时增加 Pause on exceptions 功能

react\packages\shared\ReactErrorUtils.js

用于存储错误以及是否发生错误的状态

// Used by Fiber to simulate a try-catch.
let hasError: boolean = false;
let caughtError: mixed = null;
复制代码

需要重新抛出的错误以及重新抛出的状态

// Used by event system to capture/rethrow the first error.
let hasRethrowError: boolean = false;
let rethrowError: mixed = null;
复制代码

reporter 对象上的 onError 方法提供一个借口改变错误的状态以及存储捕获到的错误对象

const reporter = {
  onError(error: mixed) {
    hasError = true;
    caughtError = error;
  },
};
复制代码

错误系统入口函数,用于捕获第一个错误

invokeGuardedCallback 的封装,当执行完 invokeGuardedCallback 之后,通过 hasError 判断是否发生了错误,并调用 clearCaughtError 返回错误对象,并清空 hasError、caughtError 。然后将返回的错误存储到 rethrowError ,并改变 hasRethrowError 的状态。

function invokeGuardedCallbackAndCatchFirstError(name,func,context,a,b,c,d,e,f) {
    invokeGuardedCallback.apply(this, arguments);
    if (hasError) {
        const error = clearCaughtError();
        if (!hasRethrowError) {
          hasRethrowError = true;
          rethrowError = error;
        }
    }
}
export function clearCaughtError() {
  if (hasError) {
    const error = caughtError;
    hasError = false;
    caughtError = null;
    return error;
  } else {
    invariant(
      false,
      'clearCaughtError was called but no error was captured. This error ' +
        'is likely caused by a bug in React. Please file an issue.',
    );
  }
}
复制代码

调用传入函数 func 的准备

invokeGuardedCallbackImpl 的封装,利用 applythis 指向 reporter ,用于当 invokeGuardedCallbackImpl 函数发生错误的时候,调用 reporter 上的接口 onError ,将错误对象存储到最外层 ReactErrorUtils.js 文件模块的 hasError、caughtError 变量上。

export function invokeGuardedCallback(name,func,context,a,b,c,d,e,f) {
    hasError = false;
    caughtError = null;
    invokeGuardedCallbackImpl.apply(reporter, arguments);
}
复制代码

直接执行的 funcinvokeGuardedCallbackImpl

invokeGuardedCallbackImpl 在不同的环境下有不同的实现:

  • 在生产环境下利用 try ... catch 捕获错误,并存储错误到 hasError、caughtError 上。
  • 在开发环境下利用自定义事件模拟了 try ... catch 功能,同时具备在开发 工具 中在所有异常发生的地方自动断点的功能。如果用 try ... catch ,那么 try 块语句中出现的错误不会断点暂停,因为这个异常被 catch 捕获了,除非在chrome下将 Pause on exceptions 打开。
Because React wraps all user-provided functions in invokeGuardedCallback, and the production version of invokeGuardedCallback uses a try-catch, all user exceptions are treated like caught exceptions, and the DevTools won't pause unless the developer takes the extra step of enabling pause on caught exceptions.

下面主要分析 invokeGuardedCallbackImpl

react\packages\shared\invokeGuardedCallbackImpl.js

生产环境:利用try-catch实现错误捕获

let invokeGuardedCallbackImpl = function(name,func,context,a,b,c,d,e,f){
  const funcArgs = Array.prototype.slice.call(arguments, 3);
  try {
    func.apply(context, funcArgs);
  } catch (error) {
    this.onError(error);
  }
};
复制代码

arguments 转成数组,然后执行传入的函数 func ,执行上下文指定为传入的 context 。如果 func 执行过程中发生错误,抛出的错误被捕获并通过 this.onError 传入最外层的变量上。这里的 this 指向的是 reporter

开发环境:模拟try-catch

const fakeNode = document.createElement('react');
const invokeGuardedCallbackDev = function(name,func,context,a,b,c,d,e,f){
    let windowEvent = window.event;
    const windowEventDescriptor = Object.getOwnPropertyDescriptor(
        window,
        'event',
    );
    const funcArgs = Array.prototype.slice.call(arguments, 3);

    /**
    * 给window添加error事件监听函数,未捕获的错误都会经过这里
    */
    let error;
    let didSetError = false;
    let isCrossOriginError = false;
    function handleWindowError(event) {
        error = event.error;
        didSetError = true;
        if (error === null && event.colno === 0 && event.lineno === 0) {
            // 当加载自不同域的脚本中发生语法错误时,为避免信息泄露,语法错误的细节将不会报告,而代之简单的"Script error."
            //  event:
            // message:错误信息(字符串)。可用于HTML onerror=""处理程序中的event。
            // source:发生错误的脚本URL(字符串)
            // lineno:发生错误的行号(数字)
            // colno:发生错误的列号(数字)
            // error:Error对象(对象)
            isCrossOriginError = true;
        }
        if (event.defaultPrevented) {
            if (error != null && typeof error === 'object') {
                try {
                    error._suppressLogging = true;
                } catch (inner) {
                    // Ignore.
                }
            }
        }
    }
    window.addEventListener('error', handleWindowError);

    /**
     * 利用createEvent创建自定义事件event,并利用initEvent指定名称,然后给DOM fakeNode添加evtType事件监听函数
     */
    let didError = true;
    const evtType = `react-${name ? name : 'invokeguardedcallback'}`;
    const evt = document.createEvent('Event');
    evt.initEvent(evtType, false/*false表示阻止该事件向上冒泡*/, false/*false表示该事件默认动作不可取消*/);
    function callCallback() {
        fakeNode.removeEventListener(evtType, callCallback, false);
        if (
            typeof window.event !== 'undefined' &&
            window.hasOwnProperty('event')
        ) {
            window.event = windowEvent;
        }

        func.apply(context, funcArgs);
        didError = false;
    }
    fakeNode.addEventListener(evtType, callCallback, false);

    /**
     * 手动触发fakeNode上的evt事件,此时会先执行传入的func函数,如果发生错误会触发window上的error事件
     */
    fakeNode.dispatchEvent(evt);


    /**
     * 还原window.event
     */
    if (windowEventDescriptor) {
        Object.defineProperty(window, 'event', windowEventDescriptor);
    }

    /**
     * 调用onError记录error
     */
    if (didError) {
        if (!didSetError) {
            error = new Error(
                'An error was thrown inside one of your components'
            );
        } else if (isCrossOriginError) {
            error = new Error(
                "A cross-origin error was thrown. React doesn't have access to ",
            );
        }
        this.onError(error);
    }

    // Remove our event listeners
    window.removeEventListener('error', handleWindowError);
};
invokeGuardedCallbackImpl = invokeGuardedCallbackDev;
复制代码

chromePause on exceptions 功能

操作: f12 -> Source Tab -> 点击 Pause on exceptions 暂停图标 -> 图标变成蓝色,表明启用了在未捕获到的异常出现的时候断点的功能。

勾选 Pause On Caught Exceptions , 能够在捕获到异常的情况下也断点。

try{
    throw'a exception';
}catch(e){
    console.log(e);
}
复制代码

上面 try 里面的代码会遇到异常,但是后面的 catch 代码能够捕获该异常。如果是所有异常都中断(勾选了 Pause On Caught Exceptions ),那么代码执行到会产生异常的 throw 语句时就会自动中断;而如果是仅遇到未捕获异常才中断,那么这里就不会中断。一般我们会更关心遇到未捕获异常的情况。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java夜未眠

Java夜未眠

蔡学镛 / 电子工业出版社 / 2003-4 / 20.00元

本书是一本散文集。作为一名资深程序设计师,作者走笔清新面独特,简练俏皮的文字下,是作者对工作,对人生的理性思考。书中收录的文章内容贴近程序员的生活,能令读者产生强烈共鸣。此外,书中的部分文章也以轻松的风格剖析了学习Java技术时的常见问题,并以专家眼光和经验推荐介绍了一批优秀的技术书籍,旨在帮助读者兴趣盎然地学习Java。一起来看看 《Java夜未眠》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具