“Script error.”是一個常見錯誤,但由於該錯誤不提供完整的報錯資訊(錯誤堆棧),問題排查往往無從下手。本文提出了該錯誤的產生原因和解決辦法,以及在ARMS中忽略該錯誤的方法。
“Script error.”的產生原因
“Script error.”有時也被稱為跨域錯誤。當網站請求並執行一個託管在第三方網域名稱下的指令碼時,就可能遇到該錯誤。最常見的情形是使用CDN託管JS資源。
為了更好地理解,假設以下HTML頁面部署在http://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檔案拋出的具體錯誤資訊,這樣做可以有效避免敏感資訊無意中被不受控制的第三方指令碼捕獲。因此,瀏覽器只允許同域下的指令碼捕獲具體錯誤資訊,而其他指令碼只知道發生了一個錯誤,但無法獲知錯誤的具體內容。更多資訊,請參見Webkit源碼。
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(Cross Origin Resource Sharing)
為了跨域捕獲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
這個備選方案。
在之前的樣本HTML頁面中加入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語句中手動上報捕獲的異常,更多資訊,請參見前端介面說明。
__bl.error(error, pos);
try catch
方法可以捕獲部分異常,但推薦採用解法1。如何在ARMS中忽略“Script error.”?
“Script error.”往往來源於第三網域名稱下的指令碼,如果不影響應用的運行,您可以選擇通過ARMS前端監控的SDK參數ignore忽略該錯誤。
ignore
可忽略指定JS錯誤的上報,其配置項的值是一個對象,包含3個屬性:ignoreUrls、ignoreApis和ignoreErrors。預設值如下:
ignore: {
ignoreUrls: [],
ignoreApis: [],
ignoreErrors: []
},
針對“Script error.”,使用ignoreErrors即可。ignoreErrors表示忽略某些JS錯誤,符合該規則的JS錯誤不會上報。值可以是String、RegExp、Function或者以上三種類型組成的數組。樣本:
__bl.setConfig({
ignore: {
ignoreErrors: /^Script error\.?$/
}
});