全局错误捕获的实现方案

在JavaScript开发中,错误处理是保证应用健壮性的关键环节。全局错误捕获能够帮助我们集中处理应用中未捕获的异常,避免程序崩溃并提供更好的用户体验。本文将介绍几种实现全局错误捕获的方案。

1. window.onerror

最基础的全局错误捕获方式是通过window.onerror事件处理器:

javascript 复制代码
window.onerror = function(message, source, lineno, colno, error) {
    console.error('全局捕获的错误:', {
        message,    // 错误信息
        source,     // 发生错误的脚本URL
        lineno,     // 错误所在行号
        colno,      // 错误所在列号
        error       // Error对象
    });
    
    // 返回true可以阻止浏览器默认的错误处理行为
    return true;
};

注意window.onerror无法捕获语法错误和跨域脚本错误。

2. window.addEventListener('error')

更现代的替代方案是使用事件监听器:

javascript 复制代码
window.addEventListener('error', function(event) {
    console.error('全局捕获的错误:', {
        message: event.message,
        filename: event.filename,
        lineno: event.lineno,
        colno: event.colno,
        error: event.error
    });
    
    // 阻止错误继续传播
    event.preventDefault();
}, true);  // 使用捕获阶段

这种方式可以捕获资源加载失败的错误(如图片、脚本等)。

3. 未处理的Promise拒绝

对于Promise中的未捕获错误,可以使用unhandledrejection事件:

javascript 复制代码
window.addEventListener('unhandledrejection', function(event) {
    console.error('未处理的Promise拒绝:', {
        reason: event.reason,  // 拒绝原因
        promise: event.promise // 相关的Promise对象
    });
    
    // 阻止默认行为(在控制台输出错误)
    event.preventDefault();
});

4. 框架特定的错误处理

主流前端框架也提供了自己的错误处理机制:

React错误边界(Error Boundaries)

javascript 复制代码
class ErrorBoundary extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false };
    }

    static getDerivedStateFromError(error) {
        return { hasError: true };
    }

    componentDidCatch(error, errorInfo) {
        // 可以在这里记录错误
        logErrorToService(error, errorInfo);
    }

    render() {
        if (this.state.hasError) {
            return <h1>Something went wrong.</h1>;
        }
        return this.props.children; 
    }
}

// 使用方式
<ErrorBoundary>
    <MyComponent />
</ErrorBoundary>

Vue错误处理

javascript 复制代码
Vue.config.errorHandler = function(err, vm, info) {
    console.error(`Vue错误: ${err.toString()}\n信息: ${info}`);
    // 可以在这里发送错误到监控服务
};

5. 综合实现方案

一个完整的全局错误处理方案可以结合上述多种方式:

javascript 复制代码
// 全局错误处理配置
const setupGlobalErrorHandling = () => {
    // JavaScript运行时错误
    window.addEventListener('error', (event) => {
        handleError(event.error || new Error(event.message));
        event.preventDefault();
    }, true);
    
    // 未处理的Promise拒绝
    window.addEventListener('unhandledrejection', (event) => {
        handleError(event.reason);
        event.preventDefault();
    });
    
    // 框架特定的错误处理
    if (typeof Vue !== 'undefined') {
        Vue.config.errorHandler = (err, vm, info) => {
            handleError(err, { component: vm, info });
        };
    }
    
    // 其他框架的错误处理可以在这里添加
};

// 统一的错误处理函数
const handleError = (error, context = {}) => {
    // 1. 控制台输出
    console.error('全局捕获的错误:', error, context);
    
    // 2. 发送到错误监控服务
    sendErrorToMonitoringService(error, context);
    
    // 3. 显示用户友好的错误提示
    showUserFriendlyError();
};

// 初始化
setupGlobalErrorHandling();

最佳实践

  1. 生产环境与开发环境区分:开发环境可以显示详细错误信息,生产环境则应显示友好提示并记录完整错误

  2. 错误上报:将错误信息发送到后端服务进行收集和分析

  3. 避免循环错误:确保错误处理逻辑本身不会抛出错误

  4. 性能考虑:错误处理逻辑应尽量轻量,避免阻塞主线程

  5. 用户反馈:提供用户反馈错误的渠道,如"报告问题"按钮

通过合理实现全局错误捕获,可以显著提升应用的稳定性和可维护性,同时为用户提供更好的体验。