前端页面全局异常处理

一、背景和意义

在前端开发中,为了及时地发现程序问题,或者排查一些不易重现的问题,需要在页面出错的时候,上报错误信息,这就需要在前端页面中增加全局的异常处理。本文给出捕捉页面异常并上报错误信息的示例。

二、代码示例

2.1 捕捉普通的JS程序错误

对于普通的JS程序错误,可以通过设置window.onerror方法捕捉,示例代码如下:

html 复制代码
<script>
    const reportError = function(msg) {
        // 这里用简单的日志输出作为上报错误信息的示例,实际中应该是要请求后端接口的
        console.log("模拟上报错误信息:" + msg);
    };
    window.onerror = function(msg, url, lineNo, columNo, error) {
        reportError(error);   // 最后1个参数error涵盖了前面4个参数的信息,只用最后一个即可
    };
    window.undefinedMethod.invoke();  // 这里调用一个不存在的方法,预期会报错
</script>

chrome开发者工具控制台显示结果如下:

在上图中上报的信息中,哪一行代码报错这一信息丢失了。这是因为Error对象直接转string会丢失哪一行代码错误这一信息。而Error对象的stack属性保留了这一信息:

html 复制代码
<script>
    const reportError = function(msg) {
        // 这里用简单的日志输出作为上报错误信息的示例,实际中应该是要请求后端接口的
        console.log("模拟上报错误信息:" + msg);
    };
    window.onerror = function(msg, url, lineNo, columNo, error) {
        reportError(error.stack);   // 最后1个参数error涵盖了前面4个参数的信息,只用最后1个即可
    };
    window.undefinedMethod.invoke();  // 这里调用一个不存在的方法,预期会报错
</script>

chrome开发者工具控制台显示结果如下:

2.2 捕捉资源加载错误

继续前面的示例,增加一段HTML代码,加载不存在的图片、JS文件以及CSS文件:

html 复制代码
<script>
    const reportError = function(msg) {
        // 这里用简单的日志输出作为上报错误信息的示例,实际中应该是要请求后端接口的
        console.log("模拟上报错误信息:" + msg);
    };
    window.onerror = function(msg, url, lineNo, columNo, error) {
        reportError(error.stack);   // 最后1个参数error涵盖了前面4个参数的信息,只用最后1个即可
    };
    window.undefinedMethod.invoke();  // 这里调用一个不存在的方法,预期会报错
</script>
<img src="invalidImage.png" alt="invalidImage" />
<script src="invalidJs.js"></script>
<link rel="stylesheet" type="text/css" href="invalidCss.css">

chrome开发者工具控制台显示结果如下:

从上图中可以看出,window.onerror不会捕捉资源加载中出现的错误。为了捕捉资源加载的错误,应该使用addEventListener,程序修改为:

html 复制代码
<script>
    const reportError = function(msg) {
        // 这里用简单的日志输出作为上报错误信息的示例,实际中应该是要请求后端接口的
        console.log("模拟上报错误信息:" + msg);
    };
    window.addEventListener('error', function(event) {
        if (event.error) {  // 如果是JS代码错误走这里
            reportError(event.error.stack);
        } else if (event.target.src) {    // JS和图片资源加载错误走这里
            reportError("fail to load resource: " + event.target.src);
        } else if (event.target.href) {   // CSS图片资源加载错误走这里
            reportError("fail to load resource: " + event.target.href);
        }
    }, true);
    window.undefinedMethod.invoke();  // 这里调用一个不存在的方法,预期会报错
</script>
<img src="invalidImage.png" alt="invalidImage" />
<script src="invalidJs.js"></script>
<link rel="stylesheet" type="text/css" href="invalidCss.css">

运行结果如下:

2.3 捕捉console.error

有时候程序对错误做了try...catch操作,然后使用console.error打印错误信息。有时候这些错误信息也是有必要上报的,但这些错误不会被前面的addEventListener捕捉到。我们可以用如下代码做一下验证:

html 复制代码
<script>
    const reportError = function(msg) {
        // 这里用简单的日志输出作为上报错误信息的示例,实际中应该是要请求后端接口的
        console.log("模拟上报错误信息:" + msg);
    };
    window.addEventListener('error', function(event) {
        if (event.error) {  // 如果是JS代码错误走这里
            reportError(event.error.stack);
        } else if (event.target.src) {    // JS和图片资源加载错误走这里
            reportError("fail to load resource: " + event.target.src);
        } else if (event.target.href) {   // CSS图片资源加载错误走这里
            reportError("fail to load resource: " + event.target.href);
        }
    }, true);
    try {
        window.undefinedMethod.invoke0();  // 调用一个不存在的方法,预期会报错,但做了catch
    } catch (err) {
        console.error(err);
    }
    window.undefinedMethod.invoke();  // 这里调用一个不存在的方法,预期会报错
</script>
<img src="invalidImage.png" alt="invalidImage" />
<script src="invalidJs.js"></script>
<link rel="stylesheet" type="text/css" href="invalidCss.css">

运行结果如下:

显然,调用invoke0出错并没有被上报。为了上报调用invoke0的错误信息,需要重写console.error方法:

html 复制代码
<script>
    const reportError = function(msg) {
        // 这里用简单的日志输出作为上报错误信息的示例,实际中应该是要请求后端接口的
        console.log("模拟上报错误信息:" + msg);
    };
    const oldError = console.error;
    console.error = function () {     // 重写console.error方法,上报其打印的错误信息
        reportError(Array.from(arguments).map(i => i instanceof Error ? i.stack : i.toString()));
        return oldError.apply(console, arguments);
    };
    window.addEventListener('error', function(event) {
        if (event.error) {  // 如果是JS代码错误走这里
            reportError(event.error.stack);
        } else if (event.target.src) {    // JS和图片资源加载错误走这里
            reportError("fail to load resource: " + event.target.src);
        } else if (event.target.href) {   // CSS图片资源加载错误走这里
            reportError("fail to load resource: " + event.target.href);
        }
    }, true);
    try {
        window.undefinedMethod.invoke0();  // 这里调用一个不存在的方法,预期会报错,但做了catch
    } catch (err) {
        console.error(err);
    }
    window.undefinedMethod.invoke();  // 这里调用一个不存在的方法,预期会报错
</script>
<img src="invalidImage.png" alt="invalidImage" />
<script src="invalidJs.js"></script>
<link rel="stylesheet" type="text/css" href="invalidCss.css">

运行结果如下,invoke0调用出错的信息也被上报了:

相关推荐
benbenAItalk10 小时前
数字人口播视频的批量生产实践:素材规范、任务编排与质量验收
java·前端·音视频
2601_9628857210 小时前
如何用 Python 扫描 A 股跳空缺口并统计缺口回补概率?
java·前端·python
西瓜太郎123411 小时前
Claude Code、Codex CLI、Gemini CLI 能否共用一枚 Key?先看协议选择矩阵
前端·api 网关·claude code·gemini cli·codex cli
陈随易13 小时前
在Finch用了62亿词元,我认为这是新一代Agent工具之神
前端·人工智能·后端
水域安全老周13 小时前
水趣钓鱼救生衣专利拆解:两级锁紧如何解决落水人衣分离
java·前端·网络
计算机魔术师14 小时前
Anthropic CEO突然喊踩刹车,OpenAI罕见力挺:AI这辆车不能只踩油门了
前端
wing9814 小时前
从codex转战workbuddy使用一周的感受
前端·人工智能·后端
EatFan14 小时前
Java接入支付宝 JSAPI 支付保姆教程(二):流程讲解与前后端代码讲解
前端·spring boot·后端·微信小程序·小程序·uni-app
梦想平凡15 小时前
百游棋牌源代码开发搭建教程(五):房间创建、座位分配与请求幂等实现
前端·javascript·数据库·源代码管理
IT_陈寒15 小时前
Redis的Set操作居然能把我的服务整挂了?
前端·人工智能·后端