在前端開發工作中,除了專案開發保質保量上線以外,專案的資料監控也應該配套起來,確保線上的正常運轉。如上報 pv 監控專案是否正常運轉;測速上報反應專案質量;指令碼錯誤監控作為監控中重要一環,當頁面發生報錯的時候,通過上報錯誤資訊,能及時發現存在問題,修復優化、減少損失。
本文基於在手Q家校群前端指令碼錯誤量優化的方案,致力於打造極致的指令碼錯誤優化。
作為首篇,主要講解基礎的指令碼錯誤監控和上報方式,以及常會遇到的 Script error. 的產生原因和處理方法。
監控上報
指令碼錯誤主要有兩類:語法錯誤、執行時錯誤。
監控的方式主要有兩種:try-catch、window.onerror。
監控方式
示例 · try-catch
1 2 3 4 5 6 |
try { test // <- throw error } catch(e){ console.log('執行時錯誤資訊 '); console.log(e); } |
通過給程式碼塊進行 try-catch 包裝,當程式碼塊出錯時 catch 將能捕獲到錯誤資訊,頁面也將繼續執行。
當發生語法錯誤或非同步錯誤時,則無法正常捕捉。
示例 · try-catch (語法報錯)
1 2 3 4 5 6 |
try { function empty() // <- throw error 語法錯誤 } catch(e){ console.log('語法錯誤資訊 ↙'); console.log(e); } |
無法捕捉錯誤
示例 · try-catch (非同步錯誤)
1 2 3 4 5 6 7 8 |
try { setTimeout(function() { test // <- throw error 非同步錯誤 },0) } catch(e){ console.log('非同步錯誤資訊 ↙'); console.log(e); } |
無法捕捉錯誤
語法錯誤無法在 try-catch 中進行捕抓、而非同步報錯則可以通過為非同步函式塊再包裝一層 try-catch,增加標識資訊來配合定位,可以用工具來進行處理,這裡不展開。
示例 · window.onerror
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/** * @param {String} msg 錯誤資訊 * @param {String} url 出錯檔案 * @param {Number} row 行號 * @param {Number} col 列號 * @param {Object} error 錯誤詳細資訊 */ window.onerror = function (msg, url, row, col, error) { console.log('onerror 錯誤資訊 ↙'); console.log({ msg, url, row, col, error }) }; test // <- throw error |
window.onerror 能捕捉到當前頁面的語法錯誤或執行時報錯,是十分強大的。
那麼try-catch 是否不再需要呢?其實並不是。
在使用過程中的體會:onerror 主要用來捕獲預料之外的錯誤,而 try-catch 則可以用在預知情況下監控特定錯誤,兩種形式結合使用更加高效。
上報方式
監控錯誤拿到了報錯資訊,接下來則是將捕抓的錯誤資訊傳送到資訊收集平臺上,傳送的形式主要有兩種:
- 通過Ajax傳送資料
- 動態建立 img 標籤的形式
示例 · 動態建立 img 標籤進行上報
1 2 3 4 |
function report(msg, level) { var reportUrl = "http://localhost:8055/report"; new Image().src = reportUrl + '?msg=' + msg; } |
監控上報整體流程
監控報錯,並將捕捉到的錯誤資訊上報給資料收集平臺,如下圖
錯誤資訊分析 · Script error
有了監控了後,就可以在收集平臺上進行檢視指令碼錯誤量的日誌統計。
發現佔據榜首的錯誤資訊 “Script error.” 具有非常高的比例,沒有無具體的錯誤資訊,無法定位問題,而這是怎麼產生的呢?
產生 Script error 的原因
翻看在 webkit 的原始碼可以看到 “Script error.” 是瀏覽器在同源策略限制下所產生的。瀏覽器出於安全上的考慮,當頁面引用的非同域的外部指令碼中丟擲了異常,此時本頁面無許可權獲得這個異常詳情, 將輸出 Script error 的錯誤資訊。
優化 Script error
Script error 來自同源策略的影響,那麼解決的方案之一是進行資源的同源化,另外也可以利用跨源資源共享機制( CORS )。
方案一:同源化
- 將js程式碼內聯到html檔案中
- 將js檔案與html檔案放到同一域名下
以上兩種方式能夠簡單直接地解決問題,但也可能帶來其他影響,如內聯資源不好利用檔案快取,同域無法充分利用cdn優勢等等。
方案二:跨源資源共享機制( CORS )
跨源資源共享 ( CORS )機制讓Web應用伺服器能支援跨站訪問控制,從而能夠安全地跨站資料傳輸。主要是通過給請求帶上特定頭資訊,伺服器實現了CORS介面,就可以跨源通訊,從而能夠看到具體報錯資訊。
1. 為頁面上script標籤新增crossorigin屬性。
1 |
<script src="http://127.0.0.1:8077/main.js" crossorigin></script> |
增加 crossorigin 屬性後,瀏覽器將自動在請求頭中新增一個 Origin 欄位,發起一個 跨來源資源共享 請求。Origin 向服務端表明了請求來源,服務端將根據來源判斷是否正常響應。
2. 響應頭中增加 Access-Control-Allow-Origin 來支援跨域資源共享。
Access-Control-Allow-Origin: * 表示通過該跨域請求,且該資源可以被任意站點跨站訪問。而當該資源僅允許來自 http://127.0.0.1:8066 的跨站請求,其它站點都不能跨站訪問時,將可以返回:
1 |
Access-Control-Allow-Origin:http://127.0.0.1:8066 |
3. 指定域名的 Access-Control-Allow-Origin 的響應頭中需帶上Vary:Origin。
Vary 欄位的作用在於為快取伺服器提供快取規則及快取篩選的依據。當增加 Vary:Origin 響應頭後,快取伺服器將會按照 Origin 欄位的內容,快取不同版本,在請求響應時根據請求頭中的 Origin 決定是否能夠使用快取響應。
舉例 · 不加 Vary 將存在錯誤命中快取的問題
上圖中,第一個請求(Origin: 127.0.0.1:8066)響應被瀏覽器快取了,當第二個請求(Origin: 127.0.0.1:8888)發起,被錯誤命中了前一個請求的快取,收到了 Access-Control-Allow-Origin:http://127.0.0.1:8066 的響應時,將導致資源載入失敗。
所以當 Access-Control-Allow-Origin 不是返回為 * 時,需要加上 Vary 返回頭來避免引快取導致的許可權問題。
跨域指令碼報錯產生 Script error. 通過以上方式進行處理後將能夠捕獲到具體的報錯資訊了。在 NodeJS 的實現中主要通過新增以下程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
pp.use(function *(next){ // 拿到請求頭中的 Origin var requestOrigin = this.get('Origin'); if (!requestOrigin) { // 不存在則忽略 return yield next; } // 設定 Access-Control-Allow-Origin: Origin this.set('Access-Control-Allow-Origin', requestOrigin); // 設定 Vary: Origin this.vary('Origin'); return yield next; }); |
以上為本文所有內容,兄弟篇:( coming soon.. )