標籤:

React16 中的異常處理(前端監控實現)

React15 及更早版本的錯誤表現

在過去,組件內部的JavaScript錯誤總是擾亂組件狀態以至於在下一次渲染中出現一些奇怪的錯誤,而且這些錯誤常常由應用中一些更早的錯誤引起,然而 React 沒有提供一種在組件內部解決這些問題的優雅方案,也不能從錯誤中恢復

認識 Error Boundaries

用戶界面某處的 JavaScript 錯誤不應該使整個 APP 崩潰,為了解決這個問題,React16 引進了一個新的概念 —— Error Boundaries

Error Boundaries 可以捕獲在其子組件樹里拋出的任何錯誤,列印這些錯誤,並且返回一個展示錯誤的界面而不是崩潰掉的頁面。Error Boundaries 可以在渲染過程中、在生命周期方法中、以及其子組件的 constructor 中捕獲錯誤

添加了一個叫做 componentDidCatch(error, info) 的新生命周期方法的組件就叫做 Error Boundaries

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>

componentDidCatch()方法就像是為組件定製的 JavaScript 中的 catch {} 代碼塊,只有通過 class 定義的組件才能成為 Error Boundaries,在實踐中,絕大多數時候你都希望只定義一個 Error Boundary 然後在應用的各個角落使用它

請注意 Error Boundaries 只能捕捉組件樹中位於它下方的組件中的錯誤

一個 Error Bundary 不能捕獲自己內部拋出的錯誤,如果一個 Error Bundary 沒有成功的渲染錯誤信息,那麼這個錯誤將傳播到離它最近的上層 Error Bundary 中,這也類似於 catch {} 代碼塊在 JavaScript 中的工作方式

在線示例

你可以試試【這個示例】

Error Boundaries 應該放置在哪兒?

Error Boundaries 的拆分粒度完全取決於你自己,你可以封裝頂層的路由組件用來顯示錯誤信息給用戶,就像應用崩潰時伺服器端框架所做的那樣。你也可以在 Error Bundary 中封裝一些小工具用來阻止整個應用的崩潰

未捕獲錯誤的新行為

這個變化有一個很重要的含義:

對 React16 來說,一個未捕獲的錯誤會導致整個應用不能被掛載

我們曾就此展開爭論,但在實踐中我們發現與其展示一個錯誤的界面,還不如把它完全移除。比如說,一個顯示錯誤的通訊軟體可能會導致用戶把訊息發送給錯誤的人。類似的,一個展示錯誤金額的支付軟體還不如什麼也不展示

這個變化意味著當你遷移到 React16 ,你可能會發現很多以前沒有被發現的錯誤,增加 Error Boundaries 可以增強你的應用發生錯誤時的用戶體驗

Facebook 把側邊欄,信息欄,對話框,和信息輸入框放到了不同的 Error Boundaries 中,這樣一來,當一個地方發生錯誤,其餘的地方仍然可以正常工作

我們也建議你使用 JS 錯誤報告服務(或者弄一個你自己的),這樣你就可以及時獲知應用中未處理的錯誤,並修復它們

組件堆疊信息

開發模式中 React16 會在控制台列印出渲染過程中發生的所有錯誤,哪怕應用偶然地吞併了它們。除了錯誤信息和 JavaScript 堆棧,它還提供組件的堆疊信息,所以你就可以精確地知道到底是應用的哪個地方出了錯誤

你還可以在組件堆疊信息中找到文件名稱和代碼行數,【Create React App】項目默認提供這個功能

如果你不打算使用 Create React App ,你也可以把【這個補丁】手動加入到你的 babel 配置文件中

為什麼不用現成的 try/catch ?

try/catch 適用於命令:

try { showButton();} catch (error) { // ...}

然而,React 組件是陳述性的,它指明要渲染的內容

<Button />

Error Boundaries 保留了 React 的陳述性,如同你所期望一樣工作。比如,就算一個componentDidUpdate生命周期函數因為一個組件內部很深的setState方法導致了錯誤,它也會正確地被傳遞到離它最近的 Error Bundary 處

和 React15 的不同

React15 包含了非常有限的對 Error Boundaries 的支持,它也有一個不同的方法名字:unstable_handleError。這個方法不管用了,你應該從 React16 beta 版首次釋出就把它升級到componentDidCatch

我們還為你提供了一個【代碼模板】


推薦閱讀:

React V16 錯誤處理(componentDidCatch 示例)
前端新人的迷茫?
[上海] 招前端,移動端,小程序開發(多圖)
React把PropTypes放到一個獨立包
前後端分離部署,腳手架環境下開發的前端代碼部署流程是怎樣的?

TAG:React |