“Script error.” 多是你遇到的最神秘的错误之一, 最让人抓狂的是这种错误没有提供完整的报错信息(错误堆栈), 让排查无从下手.
产生Script Error的缘由
“Script error.” 有时也被称为跨域错误. 当网站请求并执行一个托管在第三方域名下的脚本,就可能抛出 "Script error." 最多见的状况是采用CDN托管JS资源.
为了更好地理解“Script error.”, 假设有以下HTML页面,部署在
test.com 域名下
<!doctype html>
<html>
<head>
<title>Test page in http://test.com</title>
</head>
<body>
<script src="http://another-domain.com/app.js"></script>
<script>
window.onerror = function (message, url, line, column, error) {
console.log(message, url, line, column, error);
}
foo(); // 调用app.js中定义的foo方法
</script>
</body>
</html>
复制代码
假定foo方法中的内容以下, 调用了一个未被定义的bar方法
// another-domain.com/app.js
function foo() {
bar(); // ReferenceError: bar is not a function
}
复制代码
页面运行以后,捕获到的异常信息以下:
"Script error.", "", 0, 0, undefined
复制代码
其实这并非一个JavaScript bug, 基于安全考虑浏览器有意隐藏其它域JS文件抛出的具体错误信息。这样能够有效避免敏感信息无心中被第三方(不受控制的)脚本捕获到,所以,浏览器只容许同域下的脚本捕获具体的错误信息。其它脚本只知道发生了一个错误,而不知具体发生了什么错误。
bool ScriptExecutionContext::sanitizeScriptError(String& errorMessage, int& lineNumber, String& sourceURL)
{
KURL targetURL = completeURL(sourceURL);
if (securityOrigin()->canRequest(targetURL))
return false;
errorMessage = "Script error.";
sourceURL = String();
lineNumber = 0;
return true;
}
复制代码
了解了“Script error.”产生的缘由, 接下来看看如何解决这类问题。
解法1: 开启CORS跨域资源共享
为了跨域捕获javaScript异常,分两步走
第一步: 添加 crossorigin=”anonymous”属性
<script src="http://another-domain.com/app.js" crossorigin="anonymous"></script>
复制代码
这一步告诉浏览器,目标脚本经过匿名方式获取。这意味着请求脚本时没有潜在的用户身份信息(如cookies、HTTP 证书等)发送到服务端
第二步: 添加跨域HTTP响应头
Access-Control-Allow-Origin: *
复制代码
或者
Access-Control-Allow-Origin: http://test.com
复制代码
注:大部分主流CDN默认添加了Access-Control-Allow-Origin属性, 以下是阿里CDN的一个示例
$ curl --head https://retcode.alicdn.com/retcode/bl.js | grep -i "access-control-allow-origin"
=> access-control-allow-origin: *
复制代码
完成上述两步以后,跨域脚本的报错就能够经过window.onerror捕获到,回到以前的案例,从新运行以后,捕获到的结果是
=> "ReferenceError: bar is not defined", "http://another-domain.com/app.js", 2, 1, [Object Error]
复制代码
可选解法2: try catch
有时候,不容易往HTTP请求响应头里面添加跨域属性,这时还能够考虑try catch这个候选方案
回到以前的案例
<!doctype html>
<html>
<head>
<title>Test page in http://test.com</title>
</head>
<body>
<script src="http://another-domain.com/app.js"></script>
<script>
window.onerror = function (message, url, line, column, error) {
console.log(message, url, line, column, error);
}
try {
foo(); // 调用app.js中定义的foo方法
} catch (e) {
console.log(e);
throw e;
}
</script>
</body>
</html>
复制代码
再次运行,输出结果以下
=> ReferenceError: bar is not defined
at foo (http://another-domain.com/app.js:2:3)
at http://test.com/:15:3
=> "Script error.", "", 0, 0, undefined
复制代码
能够看出来, try catch中的console语句输出了完整的信息, 但window.onerror中只能捕获“Script error.” 基于这个特色,能够在catch语句中,将捕获的异常手动上报。
// 参考阿里云ARMS前端监控API指南 https://help.aliyun.com/document_detail/58657.html
__bl.error(error, pos);
复制代码
尽管能够经过try catch可以捕获部分异常,但仍是推荐尽可能采用方式。