NextJS静态渲染时如何捕获客户端致命错误并上报日志
捕获Next.js静态渲染应用的客户端致命错误并上报
一、利用React错误边界(Error Boundary)
Next.js的Pages Router和App Router都支持通过错误边界捕获组件树内的渲染错误,这是拦截客户端组件级致命错误的核心方案:
Pages Router 实现
在_app.tsx中封装自定义错误边界组件:
import type { AppProps } from 'next/app' import React from 'react' class ErrorBoundary extends React.Component<{ children: React.ReactNode }, { hasError: boolean }> { constructor(props) { super(props) this.state = { hasError: false } } static getDerivedStateFromError(error: Error) { return { hasError: true } } componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { this.reportError(error, errorInfo) } reportError(error: Error, errorInfo: React.ErrorInfo) { // 自定义上报逻辑,替换为你的日志接口 fetch('/api/report-error', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: error.message, stack: error.stack, componentStack: errorInfo.componentStack, pageUrl: window.location.href, timestamp: new Date().toISOString(), }), }) } render() { if (this.state.hasError) { return <div className="error-fallback">页面加载出错,请稍后重试</div> } return this.props.children } } function MyApp({ Component, pageProps }: AppProps) { return ( <ErrorBoundary> <Component {...pageProps} /> </ErrorBoundary> ) } export default MyApp
App Router 实现(Next.js 13+)
在app/error.tsx中定义全局错误边界(这不是404页面,专门处理客户端渲染错误):
'use client' import { useEffect } from 'react' export default function Error({ error, reset }: { error: Error & { digest?: string }, reset: () => void }) { useEffect(() => { fetch('/api/report-error', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: error.message, stack: error.stack, pageUrl: window.location.href, timestamp: new Date().toISOString(), }), }) }, [error]) return ( <div> <h2>页面加载出错了</h2> <button onClick={() => reset()}>点击重试</button> </div> ) }
二、全局监听未捕获的JS错误
除了组件树内的错误,还需要监听全局层面的未捕获同步错误和Promise拒绝:
在客户端入口文件(Pages Router的_app.tsx或App Router的layout.tsx)中添加监听:
'use client' import { useEffect } from 'react' function GlobalErrorListener() { useEffect(() => { // 监听未捕获同步错误 const handleUncaughtError = (event: ErrorEvent) => { event.preventDefault() fetch('/api/report-error', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: event.error?.message || '未知全局错误', stack: event.error?.stack, type: 'uncaughtError', pageUrl: window.location.href, timestamp: new Date().toISOString(), }), }) } // 监听未处理的Promise拒绝 const handleRejection = (event: PromiseRejectionEvent) => { event.preventDefault() fetch('/api/report-error', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: event.reason?.message || 'Promise未处理拒绝', stack: event.reason?.stack, type: 'unhandledRejection', pageUrl: window.location.href, timestamp: new Date().toISOString(), }), }) } window.addEventListener('error', handleUncaughtError) window.addEventListener('unhandledrejection', handleRejection) return () => { window.removeEventListener('error', handleUncaughtError) window.removeEventListener('unhandledrejection', handleRejection) } }, []) return null } // 在Pages Router的_app.tsx中引入 function MyApp({ Component, pageProps }: AppProps) { return ( <> <GlobalErrorListener /> <ErrorBoundary> <Component {...pageProps} /> </ErrorBoundary> </> ) } // 在App Router的layout.tsx中引入 export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="zh-CN"> <body> <GlobalErrorListener /> {children} </body> </html> ) }
三、处理静态渲染的Hydrate错误
静态生成(SSG)页面在客户端hydrate时可能出现内容不匹配错误,可通过next.config.js开启调试日志,同时上述错误边界会捕获这类错误:
// next.config.js module.exports = { reactStrictMode: true, logging: { fetches: { fullUrl: true }, }, }
四、优化建议
- 给上报接口添加防抖逻辑,避免同一错误重复上报
- 上报时可附加用户标识、浏览器UA信息,提升调试效率
- 静态渲染页面的客户端错误多源于服务端/客户端内容不一致,重点检查动态数据、组件状态初始化逻辑
内容的提问来源于stack exchange,提问作者Kevin Danikowski
相关产品推荐
相关产品推荐

