本文详解如何使用window.onerror捕获JavaScript运行时错误,并重点解决跨域脚本加载时的错误信息屏蔽问题。通过配置script标签的crossorigin属性及服务端Access-Control-Allow-Origin响应头,可实现跨域JS错误的完整捕获与上报,适用于前端监控系统的构建。
window.onerror基础用法与Demo
近期有2048游戏用户反馈,在iPhone 4S、iOS 5.1设备上打开游戏后无法游玩,表现为仅有游戏面板,数字未初始化且不可移动。尝试通过微信调起Safari浏览器打开,问题依旧存在。鉴于游戏大量使用了HTML5特性,推测可能是JS报错导致。那么如何捕获此类信息呢?答案是使用window.onerror。
查阅W3C规范中关于window.onerror的方法体介绍:

这意味着,我们可以将window.onerror方法定义如下:
/**
* @param {String} errorMessage 错误信息
* @param {String} scriptURI 出错的文件
* @param {Long} lineNumber 出错代码的行号
* @param {Long} columnNumber 出错代码的列号
* @param {Object} errorObj 错误的详细信息,Anything
*/
window.onerror = function(errorMessage, scriptURI, lineNumber,columnNumber,errorObj) {
// TODO
}
但在实际使用中需注意浏览器兼容性问题,并非所有浏览器都支持参数列表中的所有参数。Chrome等浏览器作为标准草案的领跑者,可以直接使用这些参数。
因此,可以编写一个小Demo进行测试:
<!DOCTYPE html>
<html>
<head>
<title>Js错误捕获</title>
<script type="text/javascript">
/**
* @param {String} errorMessage 错误信息
* @param {String} scriptURI 出错的文件
* @param {Long} lineNumber 出错代码的行号
* @param {Long} columnNumber 出错代码的列号
* @param {Object} errorObj 错误的详细信息,Anything
*/
window.onerror = function(errorMessage, scriptURI, lineNumber,columnNumber,errorObj) {
console.log("错误信息:" , errorMessage);
console.log("出错文件:" , scriptURI);
console.log("出错行号:" , lineNumber);
console.log("出错列号:" , columnNumber);
console.log("错误详情:" , errorObj);
}
</script>
</head>
<body>
<script type="text/javascript" src="error.js"></script>
</body>
</html>
其中error.js文件的内容简单定义为:
throw new Error("出错了!"); 在浏览器中运行后,打开控制台,显示如下:

跨域脚本错误捕获问题
由此可见,这些数据均可用于上报。 当然,上述error.js与HTML页面处于同域名下。如果error.js不在同域下,情况会如何?我们将error.js的引用修改为:
<script type="text/javascript" src="https://doitbegin.duapp.com/error.js"></script> 再次打开控制台,我们看到:

此时,window.onerror方法仅捕获到一个errorMessage,且为固定字符串,毫无参考价值。查阅资料(Webkit源码)发现,在浏览器实现script资源加载时进行了同源策略判断。如果是非同源资源,errorMessage会被写死为“Script error”:

跨域错误上报解决方案
好在script标签有一个crossorigin属性,设置它可以显示比较详细的错误信息。我们尝试将script标签修改为:
<script type="text/javascript" src="https://doitbegin.duapp.com/error.js" crossorigin></script> 刷新页面,此时看到console中的输出如下:

出现这个error也不意外,既然设置了error.js为crossorigin,那error.js的HTTP Response Header也必须设置非同源可访问。为了方便设置Header,把error.js做一个小改动,更名为:error-js.php。
<?php
header('Access-Control-Allow-Origin:*');
header('Content-type:text/javascript');
?>
throw new Error('出错了');
此时刷新页面,看到console中的输出就已经正常了,所有信息都能正常捕获:

OK,技术细节分析结束!我2048游戏静态资源是放到静态域(非同源)下的,所以要想通过window.onerror捕获错误信息,就得按照上面的最后一种情况来操作了:
1、添加script的crossorigin属性
2、配置一下服务器,设置静态资源Javascript的Response为Access-Control-Allow-Origin