react中的错误处理

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

内容简介:持续更新中对
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 语句时就会自动中断;而如果是仅遇到未捕获异常才中断,那么这里就不会中断。一般我们会更关心遇到未捕获异常的情况。


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

查看所有标签

猜你喜欢:

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

Head First Rails

Head First Rails

David Griffiths / O'Reilly Media / 2008-12-30 / USD 49.99

Figure its about time that you hop on the Ruby on Rails bandwagon? You've heard that it'll increase your productivity exponentially, and allow you to created full fledged web applications with minimal......一起来看看 《Head First Rails》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具