**摘要:** 堆栈是 Debug 的关键。
<!-- more -->
- 原文:[如何优雅地查看 JS 错误堆栈?](https://cloud.tencent.com/developer/article/1365411)
- 作者:[小芭乐](https://cloud.tencent.com/developer/user/1000005)
**[Fundebug](https://www.fundebug.com/)经授权转载,版权归原作者所有。**
在前端,我们经常会通过 `window.onerror` 事件来捕获未处理的异常。假设捕获了一个异常,上报的堆栈是这个:
```js
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](https://github.com/mishoo/UglifyJS2) 反向美化成多行的代码 `prettysource`,并且同时生成 `sourcemap`
- 堆栈帧中的 `line` 和 `col` 通过 `sourcemap` [反查](https://github.com/mozilla/source-map#consuming-a-source-map),得到美化后对应的 `prettyline` 和 `prettycol`
- 将 `prettysource`、`prettyline`、`prettycol` 给到 [Monaco Editor](https://microsoft.github.io/monaco-editor/) 渲染,就可以得到上述截图的效果
说那么多,不如贴代码是吧:
```js
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 的使用源码:
```js
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
);
```
完整源码有兴趣的读者也可以下下来把玩把玩:
[js-loader.html.zip](https://ask8088-1251520898.file.myqcloud.com/draft/1000005/h7846jpv0c.zip)
源码只包含堆栈解析的实现,UI 的实现不在本文的讨论之内,用 React 随便画一画就好了。
### 关于Fundebug
[Fundebug](https://www.fundebug.com/)专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有Google、360、金山软件、百姓网等众多品牌企业。欢迎大家[免费试用](https://www.fundebug.com/team/create)!

- 如何高效地遍历超大MongoDB集合
- 深入理解 JavaScript 执行上下文和执行栈
- JavaScript 为什么要有 Symbol 类型?
- Web 性能优化:21 种优化 CSS 和加快网站速度的方法
- 深入理解 JavaScript 作用域和作用域链
- WEB 实时推送技术的总结
- 前端面试:谈谈 JS 垃圾回收机制
- TypeScript,初次见面,请多指教 ?
- 使用这些 HTTP 头保护 Web 应用
- JavaScript 的 4 种数组遍历方法: for VS forEach() VS for/in VS for/of
- 装上这几个 VSCode 插件后,上班划水摸鱼不是梦
- 一文搞懂TCP与UDP的区别
- 如何优雅地查看 JS 错误堆栈?
- 一文读懂 HTTP/2 及 HTTP/3 特性
- Web 性能优化: 图片优化让网站大小减少 62%
- 我们应该如何给需求排序?
- Web 性能优化: 使用 Webpack 分离数据的正确方法
- 2019 前端面试题汇总(主要为 Vue)
- 高效使用VSCode的9点建议
- 小程序多端框架全面测评:chameleon、Taro、uni-app、mpvue、WePY
- 灵活使用 console 让 js 调试更简单
- 深入了解浏览器存储:对比Cookie、LocalStorage、sessionStorage与IndexedDB
- JavaScript是如何工作的:引擎,运行时和调用堆栈的概述!
- Web 性能优化:Preload与Prefetch的使用及在 Chrome 中的优先级
- 如何来一次说干就干的重构 (流程篇)
- JavaScript 新语法详解:Class 的私有属性与私有方法
- JavaScript 原型的深入指南
- 一个程序员的自我修养
- 为什么HTTPS比HTTP更安全?