本文由雲+社區發表
在前端,我們經常會通過 window.onerror
事件來捕獲未處理的異常。假設捕獲了一個異常,上報的堆棧是這個:
TypeError: Cannot read property 'module' of undefined
at Object.exec (https://my.cdn.com/dest/app.efe91e855d7432e402545e7d6c25d2d9.js:16:29828)
at HTMLLIElement.<anonymous> (https://my.cdn.com/dest/app.efe91e855d7432e402545e7d6c25d2d9.js:25:6409)
at HTMLDivElement.dispatch (https://my.cdn.com/dest/vendor.eb28ded1876760b8e90973c9f4813a2c.js:1:248887)
at HTMLDivElement.y.handle (https://my.cdn.com/dest/vendor.eb28ded1876760b8e90973c9f4813a2c.js:1:245631)
這個堆棧,你看得出問題來嗎?我們發布到 CDN 的腳本文件,普遍是經過 UglifyJS 壓縮的,所以堆棧可讀性相當的差。假如有下面的一個堆棧查看工具,又如何?
堆棧查看工具
眼尖的同學,一眼就能找到問題。這里的 p[e]
出現了可能為 undefined
的情況。
這樣一個工具,大大提高了問題定位的效率。
好,這里不賣瓜,我們來看下這當中的實現原理。
堆棧工具實現原理
一步步來說的話:
-
拿到原始堆棧字符串,使用
error-stack-parser
解析為堆棧幀,每個堆棧幀包含三個最重要的字段:
url
- 源碼的 URL 地址line
- 堆棧位置行號col
- 堆棧位置列號
-
對於
url
,我們可以用於加載源碼內容,得到source
-
source 使用 UglifyJs 反向美化成多行的代碼
prettysource
,並且同時生成sourcemap
-
堆棧幀中的
line
和col
通過sourcemap
反查,得到美化后對應的prettyline
和prettycol
-
將
prettysource
、prettyline
、prettycol
給到 Monaco Editor 渲染,就可以得到上述截圖的效果
說那么多,不如貼代碼是吧:
var result = UglifyJS.minify(source, {
output: {
beautify: true
},
sourceMap: {
filename: 'pretty.js',
url: 'pretty.js.map'
}
});
var code = result.code;
var rawSourceMap = JSON.parse(result.map);
var consumerPromise = new sourceMap.SourceMapConsumer(rawSourceMap);
resolve(
consumerPromise.then(function(consumer) {
return {
code: code,
sourceMapConsumer: consumer
}
})
);
上面就是使用 UglifyJs 對壓縮代碼進行反向美化的核心代碼。下面給出 SourceMap 的使用源碼:
var code = result.code;
var consumer = result.sourceMapConsumer;
var position = consumer.generatedPositionFor({
source: '0',
line: lineNumber,
column: columnNumber
});
parent.postMessage({
event: 'js-prettify-callback',
payload: {
hash: payload.hash,
result: 'success',
prettySource: code,
prettyLineNumber: position.line,
prettyColumnNumber: position.column + 1
}
}, sourceOrigin);
完整源碼有興趣的讀者也可以下下來把玩把玩:
源碼只包含堆棧解析的實現,UI 的實現不在本文的討論之內,用 React 隨便畫一畫就好了。
此文已由作者授權騰訊雲+社區在各渠道發布
獲取更多新鮮技術干貨,可以關注我們騰訊雲技術社區-雲加社區官方號及知乎機構號