上篇文章我們大致了解了如何捕獲全局error和全局的promise error。但在前端框架中,有些它們框架本身的錯誤處理,例如React的ErrorBoundary
和Vue的onErrorCaptured
。下列就以React的ErrorBoundary
為例,看Sentry是如何結合的。
ErrorBoundary
是 React在處理渲染階段錯誤的一種方式。當React組件樹中的某個組件渲染、生命週期函數錯誤、或者其他組件級別發生錯誤時,ErrorBoundary
可以捕獲錯誤、然後將其組件節點以及它以下的所有節點都從渲染流程中移除,替換成宣告的fallback
組件。如:
import { ErrorBoundary } from 'react-error-boundary';
function App() {
return (
<ErrorBoundary fallback={<div>Something Wrong</div>}>
<MyComponent />
</ErrorBoundary>
);
}
這樣的方式可以確保應用程序即便出現錯誤,也不會直接崩潰,能夠展示預定義的錯誤 UI。
雖然一般情況下可以直接用react-error-boundary
這個庫來直接使用ErrorBoundary
,但為了更大程度的控制和自由度,我們也可以自己實現一個簡單的ErrorBoundary
組件。
特別一提,在近期的React 版本中,幾乎沒有組件事用class的方式來寫,官方也是建議都以函數組件來開發,就是除了ErrorBoundary
。其中最重要的就是getDerivedStateFromError
的靜態方法以及componentDidCatch
的生命週期方法:
getDerivedStateFromError
---當子組件發生錯誤時更新 state,讓組件渲染 fallback UI。componentDidCatch
---捕獲錯誤並執行自定義錯誤處理(例如記錄錯誤)。在development mode時,React 是會將錯誤訊息從ErrorBoundary
拋到全局,所以這時window.onerror
是可以獲取到相關的error資訊;但在 production mode (也就是React build過後),ErrorBoundary
會捕獲子組件的錯誤、然後拋一個簡單的錯誤訊息到全局而已,沒有相關的上下文。
因此,為了能夠捕獲更詳細的錯誤信息,Sentry 需要自己實現一個 ErrorBoundary
來攔截 React 的錯誤訊息。
根據 React 官方的示例,我們可以簡單修改,實現一個自定義的 ErrorBoundary
並將其與 跟我們寫的簡單 SDK 結合。
import React from 'react';
export default class MyErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, info) {
console.log('error:', error);
// Example "componentStack":
// in ComponentThatThrows (created by App)
// in ErrorBoundary (created by App)
// in div (created by App)
// in App
console.log('info.componentStack:', info.componentStack);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return this.props.fallback;
}
return this.props.children;
}
}
getDerivedStateFromError
---當 MyErrorBoundary
的子組件發生錯誤的時候,React會調用該方法,強制更新 MyErrorBoundary
的 hasError
state,觸發重新渲染並顯示fallback
組件。componentDidCatch
---該方法允許我們捕獲錯誤並進行自定義處理,例如通過 Sentry SDK 上報錯誤。可以直接使用剛剛寫好的MyErrorBoundary
,然後再包一層來方便調用
import MyErrorBoundary from './MyErrorBoundary';
export default class SelfSentry {
static ErrorBoundary({ children, fallback }) {
return (
<MyErrorBoundary fallback={fallback}>{children}</MyErrorBoundary>
);
}
}
export const selfSentry = new SelfSentry();
然後就可以在App.jsx
中調用這個自定義的 Error Boundary sdk 方法,並嘗試捕捉錯誤。
import { useState } from 'react';
import './App.css';
import SelfSentry, { selfSentry } from './self-sentry/index';
selfSentry.init();
const DemoError = () => {
throw new Error('Demo Component error');
};
function App() {
const [shouldMount, setShouldMount] = useState(false);
return (
<SelfSentry.ErrorBoundary fallback={<div>Something Wrong</div>}>
<div>
{shouldMount && <DemoError />}
<button onClick={() => setShouldMount((pre) => !pre)}>
mount DemoError Component
</button>
</div>
</SelfSentry.ErrorBoundary>
);
}
export default App;
這邊就可以看到SDK捕獲到了 ErrorBoundary的error資訊:
本文通過手寫一個自定義的 ErrorBoundary
並結合 Self SDK,展示了如何在 React 應用中有效地捕獲錯誤。這種集成方式在開發模式下,能夠直接捕捉完整的錯誤上下文,並顯示具體的錯誤訊息。
然而,在 production mode 中(即應用被打包後),因為 JavaScript 被壓縮、混淆,我們還需要使用 source map 來將壓縮過的錯誤堆疊還原回原始的程式碼上下文。
本文的程式碼可在Github repository中查看。