欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React如何優(yōu)雅的捕獲異常

 更新時間:2021年06月18日 10:07:23   作者:云的世界  
捕獲異常是來定位你錯誤代碼的。本文主要介紹了 React如何捕獲異常,你知道多少種方法,ErrorBoundary,ErrorBoundary-try-catch等等。本文就來詳細的介紹一下

前言

人無完人,所以代碼總會出錯,出錯并不可怕,關(guān)鍵是怎么處理。
我就想問問大家react的應(yīng)用的錯誤怎么捕捉呢? 這個時候:

  • 小白+++:怎么處理?
  • 小白++: ErrorBoundary
  • 小白+: ErrorBoundary, try catch
  • 小黑#:  ErrorBoundary, try catch, window.onerror
  • 小黑##: 這個是個嚴肅的問題,我知道N種處理方式,你有什么更好的方案?

ErrorBoundary

EerrorBoundary是16版本出來的,有人問那我的15版本呢,我不聽我不聽,反正我用16,當(dāng)然15有unstable_handleError。

關(guān)于ErrorBoundary官網(wǎng)介紹比較詳細,這個不是重點,重點是他能捕捉哪些異常。

  • 子組件的渲染
  • 生命周期函數(shù)
  • 構(gòu)造函數(shù)
  • 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>

開源世界就是好,早有大神封裝了react-error-boundary 這種優(yōu)秀的庫。
你只需要關(guān)心出現(xiàn)錯誤后需要關(guān)心什么,還以來個 Reset, 完美。

import {ErrorBoundary} from 'react-error-boundary'

function ErrorFallback({error, resetErrorBoundary}) {
  return (
    <div role="alert">
      <p>Something went wrong:</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  )
}

const ui = (
  <ErrorBoundary
    FallbackComponent={ErrorFallback}
    onReset={() => {
      // reset the state of your app so the error doesn't happen again
    }}
  >
    <ComponentThatMayError />
  </ErrorBoundary>
)

遺憾的是,error boundaries并不會捕捉這些錯誤:

  • 事件處理程序
  • 異步代碼 (e.g. setTimeout or requestAnimationFrame callbacks)
  • 服務(wù)端的渲染代碼
  • error boundaries自己拋出的錯誤

原文可見參見官網(wǎng)introducing-error-boundaries

本文要捕獲的就是 事件處理程序的錯誤。
官方其實也是有方案的how-about-event-handlers, 就是 try catch.

但是,那么多事件處理程序,我的天,得寫多少,。。。。。。。。。。。。。。。。。。。。

  handleClick() {
    try {
      // Do something that could throw
    } catch (error) {
      this.setState({ error });
    }
  }

Error Boundary 之外

我們先看看一張表格,羅列了我們能捕獲異常的手段和范圍。

異常類型 同步方法 異步方法 資源加載 Promise async/await
try/catch
window.onerror
error
unhandledrejection

try/catch

可以捕獲同步和async/await的異常。

window.onerror , error事件

    window.addEventListener('error', this.onError, true);
    window.onerror = this.onError

window.addEventListener('error') 這種可以比 window.onerror 多捕獲資源記載異常.
請注意最后一個參數(shù)是 true, false的話可能就不如你期望。
當(dāng)然你如果問題這第三個參數(shù)的含義,我就有點不想理你了。拜。

unhandledrejection

請注意最后一個參數(shù)是 true。

window.removeEventListener('unhandledrejection', this.onReject, true)

其捕獲未被捕獲的Promise的異常。

XMLHttpRequest 與 fetch

XMLHttpRequest 很好處理,自己有onerror事件。
當(dāng)然你99.99%也不會自己基于XMLHttpRequest封裝一個庫, axios 真香,有這完畢的錯誤處理機制。
至于fetch, 自己帶著catch跑,不處理就是你自己的問題了。
這么多,太難了。
還好,其實有一個庫react-error-catch 是基于ErrorBoudary,error與unhandledrejection封裝的一個組件。
其核心如下

   ErrorBoundary.prototype.componentDidMount = function () {
        // event catch
        window.addEventListener('error', this.catchError, true);
        // async code
        window.addEventListener('unhandledrejection', this.catchRejectEvent, true);
    };

使用:

import ErrorCatch from 'react-error-catch'

const App = () => {
  return (
  <ErrorCatch
      app="react-catch"
      user="cxyuns"
      delay={5000}
      max={1}
      filters={[]}
      onCatch={(errors) => {
        console.log('報錯咯');
        // 上報異常信息到后端,動態(tài)創(chuàng)建標簽方式
        new Image().src = `http://localhost:3000/log/report?info=${JSON.stringify(errors)}`
      }}
    >
      <Main />
    </ErrorCatch>)
}

export default

鼓掌,鼓掌。
其實不然: 利用error捕獲的錯誤,其最主要的是提供了錯誤堆棧信息,對于分析錯誤相當(dāng)不友好,尤其打包之后。
錯誤那么多,我就先好好處理React里面的事件處理程序。
至于其他,待續(xù)。

事件處理程序的異常捕獲

示例

我的思路原理很簡單,使用decorator來重寫原來的方法。
先看一下使用:

   @methodCatch({ message: "創(chuàng)建訂單失敗", toast: true, report:true, log:true })
    async createOrder() {
        const data = {...};
        const res = await createOrder();
        if (!res || res.errCode !== 0) {
            return Toast.error("創(chuàng)建訂單失敗");
        }
        
        .......
        其他可能產(chǎn)生異常的代碼
        .......
        
       Toast.success("創(chuàng)建訂單成功");
    }

注意四個參數(shù):

  • message: 出現(xiàn)錯誤時,打印的錯誤
  • toast: 出現(xiàn)錯誤,是否Toast
  • report: 出現(xiàn)錯誤,是否上報
  • log: 使用使用console.error打印

可能你說,這這,消息定死,不合理啊。我要是有其他消息呢。
此時我微微一笑別急, 再看一段代碼

  @methodCatch({ message: "創(chuàng)建訂單失敗", toast: true, report:true, log:true })
    async createOrder() {
        const data = {...};
        const res = await createOrder();
        if (!res || res.errCode !== 0) {
            return Toast.error("創(chuàng)建訂單失敗");
        }
       
        .......
        其他可能產(chǎn)生異常的代碼
        .......
        
       throw new CatchError("創(chuàng)建訂單失敗了,請聯(lián)系管理員", {
           toast: true,
           report: true,
           log: false
       })
       
       Toast.success("創(chuàng)建訂單成功");

    }

是都,沒錯,你可以通過拋出 自定義的CatchError來覆蓋之前的默認選項。
這個methodCatch可以捕獲,同步和異步的錯誤,我們來一起看看全部的代碼。

類型定義

export interface CatchOptions {
    report?: boolean;
    message?: string;
    log?: boolean;
    toast?: boolean;
}

// 這里寫到 const.ts更合理
export const DEFAULT_ERROR_CATCH_OPTIONS: CatchOptions = {
    report: true,
    message: "未知異常",
    log: true,
    toast: false
}

自定義的CatchError

import { CatchOptions, DEFAULT_ERROR_CATCH_OPTIONS } from "@typess/errorCatch";

export class CatchError extends Error {

    public __type__ = "__CATCH_ERROR__";
    /**
     * 捕捉到的錯誤
     * @param message 消息
     * @options 其他參數(shù)
     */
    constructor(message: string, public options: CatchOptions = DEFAULT_ERROR_CATCH_OPTIONS) {
        super(message);
    }
}

裝飾器

import Toast from "@components/Toast";
import { CatchOptions, DEFAULT_ERROR_CATCH_OPTIONS } from "@typess/errorCatch";
import { CatchError } from "@util/error/CatchError";


const W_TYPES = ["string", "object"];
export function methodCatch(options: string | CatchOptions = DEFAULT_ERROR_CATCH_OPTIONS) {

    const type = typeof options;

    let opt: CatchOptions;

    
    if (options == null || !W_TYPES.includes(type)) { // null 或者 不是字符串或者對象
        opt = DEFAULT_ERROR_CATCH_OPTIONS;
    } else if (typeof options === "string") {  // 字符串
        opt = {
            ...DEFAULT_ERROR_CATCH_OPTIONS,
            message: options || DEFAULT_ERROR_CATCH_OPTIONS.message,
        }
    } else { // 有效的對象
        opt = { ...DEFAULT_ERROR_CATCH_OPTIONS, ...options }
    }

    return function (_target: any, _name: string, descriptor: PropertyDescriptor): any {

        const oldFn = descriptor.value;

        Object.defineProperty(descriptor, "value", {
            get() {
                async function proxy(...args: any[]) {
                    try {
                        const res = await oldFn.apply(this, args);
                        return res;
                    } catch (err) {
                        // if (err instanceof CatchError) {
                        if(err.__type__ == "__CATCH_ERROR__"){
                            err = err as CatchError;
                            const mOpt = { ...opt, ...(err.options || {}) };

                            if (mOpt.log) {
                                console.error("asyncMethodCatch:", mOpt.message || err.message , err);
                            }

                            if (mOpt.report) {
                                // TODO::
                            }

                            if (mOpt.toast) {
                                Toast.error(mOpt.message);
                            }

                        } else {
                            
                            const message = err.message || opt.message;
                            console.error("asyncMethodCatch:", message, err);

                            if (opt.toast) {
                                Toast.error(message);
                            }
                        }
                    }
                }
                proxy._bound = true;
                return proxy;
            }
        })
        return descriptor;
    }
}

總結(jié)一下

利用裝飾器重寫原方法,達到捕獲錯誤的目的
自定義錯誤類,拋出它,就能達到覆蓋默認選項的目的。增加了靈活性。

  @methodCatch({ message: "創(chuàng)建訂單失敗", toast: true, report:true, log:true })
    async createOrder() {
        const data = {...};
        const res = await createOrder();
        if (!res || res.errCode !== 0) {
            return Toast.error("創(chuàng)建訂單失敗");
        }
       Toast.success("創(chuàng)建訂單成功");
       
        .......
        其他可能產(chǎn)生異常的代碼
        .......
        
       throw new CatchError("創(chuàng)建訂單失敗了,請聯(lián)系管理員", {
           toast: true,
           report: true,
           log: false
       })
    }

下一步

啥下一步,走一步看一步啦。
不,接下來的路,還很長。  這才是一個基礎(chǔ)版本。

擴大成果

@XXXCatch
classs AAA{
    @YYYCatch
    method = ()=> {
    }
}

抽象,再抽象,再抽象

再見。

寫在最后

error-boundaries
React異常處理
catching-react-errors
react進階之異常處理機制-error Boundaries
decorator
core-decorators
autobind.js

到此這篇關(guān)于React如何優(yōu)雅的捕獲異常的文章就介紹到這了,更多相關(guān)React 捕獲異常內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React Hooks使用方法全方位介紹

    React Hooks使用方法全方位介紹

    在react類組件(class)寫法中,有setState和生命周期對狀態(tài)進行管理,但是在函數(shù)組件中不存在這些,故引入hooks(版本:>=16.8),使開發(fā)者在非class的情況下使用更多react特性
    2023-03-03
  • React-Router如何進行頁面權(quán)限管理的方法

    React-Router如何進行頁面權(quán)限管理的方法

    本篇文章主要介紹了React-Router如何進行頁面權(quán)限管理的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • React組件的應(yīng)用介紹

    React組件的應(yīng)用介紹

    React組件分為函數(shù)組件與class組件;函數(shù)組件是無狀態(tài)組件,class稱為類組件;函數(shù)組件只有props,沒有自己的私有數(shù)據(jù)和生命周期函數(shù);class組件有自己私有數(shù)據(jù)(this.state) 和 生命周期函數(shù)
    2022-09-09
  • react 權(quán)限樹形結(jié)構(gòu)實現(xiàn)代碼

    react 權(quán)限樹形結(jié)構(gòu)實現(xiàn)代碼

    這篇文章主要介紹了react 權(quán)限樹形結(jié)構(gòu)實現(xiàn)代碼,項目背景react + ant design,本文結(jié)合實例代碼給大家介紹的非常詳細,感興趣的朋友一起看看吧
    2024-05-05
  • React中多語言的配置方式

    React中多語言的配置方式

    這篇文章主要介紹了React中多語言的配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • react native環(huán)境安裝流程

    react native環(huán)境安裝流程

    React Native 是目前流行的跨平臺移動應(yīng)用開發(fā)框架之一。本文介紹react native環(huán)境安裝流程及遇到問題解決方法,感興趣的朋友一起看看吧
    2021-05-05
  • react調(diào)試和測試代碼的小技巧

    react調(diào)試和測試代碼的小技巧

    在開發(fā)React應(yīng)用時,嚴格模式StrictMode可以幫助開發(fā)者捕捉到組件中的錯誤和潛在問題,安裝React Developer Tools瀏覽器擴展檢查組件的props和狀態(tài),直接修改以及分析性能,@testing-library/react和Cypress或Playwright等工具可以有效地測試React組件和執(zhí)行端到端測試
    2024-10-10
  • React?Hook?四種組件優(yōu)化總結(jié)

    React?Hook?四種組件優(yōu)化總結(jié)

    這篇文章主要介紹了React?Hook四種組件優(yōu)化總結(jié),文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價孩子,需要的朋友可以參考一下
    2022-07-07
  • React文件分段上傳實現(xiàn)方法詳解

    React文件分段上傳實現(xiàn)方法詳解

    這篇文章主要介紹了React文件分段上傳實現(xiàn)方法,將文件切成多個小的文件;將切片并行上傳;所有切片上傳完成后,服務(wù)器端進行切片合成;當(dāng)分片上傳失敗,可以在重新上傳時進行判斷,只上傳上次失敗的部分實現(xiàn)斷點續(xù)傳;當(dāng)切片合成為完整的文件,通知客戶端上傳成功
    2022-11-11
  • 詳解基于React.js和Node.js的SSR實現(xiàn)方案

    詳解基于React.js和Node.js的SSR實現(xiàn)方案

    這篇文章主要介紹了詳解基于React.js和Node.js的SSR實現(xiàn)方案,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03

最新評論