Script Error產生的緣由及解法

「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文件拋出的具體錯誤信息。這樣能夠有效避免敏感信息無心中被第三方(不受控制的)腳本捕獲到,所以,瀏覽器只容許同域下的腳本捕獲具體的錯誤信息。其它腳本只知道發生了一個錯誤,而不知具體發生了什麼錯誤。
且看 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跨域資源共享

爲了跨域捕獲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可以捕獲部分異常,但仍是推薦儘可能採用方式。
相關文章
相關標籤/搜索