前端异常解析:Source Map
目录
引子
在前端异常解析中介绍了异常的解析,这些异常信息上报后,一般也难以直接的看出什么错误源,因为正式的线上环境中,代码往往都经过了压缩混淆,异常的一些信息都是指向压缩文件。这时候可以根据 Source Map 文件追溯源文件的位置。
简介
最初的源映射格式是由 Joseph Schorr 创建,在 Closure Inspector(谷歌的公共工具)中用来开启 JavaScript 源码级别的调试优化。随着使用了源映射的项目规模扩大,格式冗余开始成为一个问题。v2 版本为了减小源映射整体大小,牺牲了一些简单性和灵活性。目前最新的版本是 v3 。更多信息见 Source Map Revision 3 。
格式
按照约定,源映射文件跟源文件拥有相同的名称,只是后缀为 .map
。比如 page.js
的产生的源映射名称是 page.js.map
。这个约定并不是强制性的。
源映射整个文件是一个 JSON 对象:
{
"version" : 3,
"file": "out.js",
"sourceRoot": "",
"sources": ["foo.js", "bar.js"],
"sourcesContent": [null, null],
"names": ["src", "maps", "are", "fun"],
"mappings": "A,AAAB;;ABCDE;"
}
- version :source map 规范版本,必须是一个正整数。
- file :可选项,转换后产生的源映射文件名。
- sourceRoot :可选项,资源更路径,在服务器上重新定位源文件和移除
sources
中重复的值有用处。这个值会预先添加到sources
字段中每一个值。如果是跟源文件相同的路径,则为空。 - sources :存放
mappings
中使用的源文件。 - sourcesContent :可选性,存放源内容。列表的顺序跟
sources
字段中顺序一致。如果一些源要按照名称检索,可能会使用null
。 - names :
mappings
中使用的一些标识名。 - mappings :记录了映射信息的字符串。
结合示例看含义就更清晰了:
更多信息见 Source Map Revision 3 。
使用
从 Chrome 39 开始,开发者工具中 Source Maps 设置项默认是开启的,更加详细的说明见这里。
本地开发的时候,类似 Webpack 构建工具都支持生成 Source Map 文件,调试的时候在 Chrome 中就可以在开发者工具 Sources 栏中看到对应源代码位置。
在正式线上环境中,需不需要生成并部署 Source Map 文件,就看各自的考虑,可以参考一下这篇文章 Should I Use Source Maps in Production? 。要说明一下,浏览器默认不会请求这类文件,不用担心带来额外的请求。如果想要方便排查线上的问题,又不想别人查看源码,服务器可以对 Source Map 文件的访问进行限制。
在正式线上环境中,没办法随时能够操作用户的电脑排查问题,出现异常的时候,假设有 Source Map 文件,该如何进行处理?由此有了下面的疑问:
- 如何检测是否有对应 Source Map 文件?
- 如何获取到 Source Map 文件?
- 如何解析 Source Map 文件?
下面针对这三个问题,依次进行解答。
如何检测是否有 Source Map 文件?
如果压缩后同时生成了 Source Map 文件,那么在压缩后代码的最后一行会是这样:
// js 文件最后一行
//# sourceMappingURL=<url>
// css 文件最后一行
/*# sourceMappingURL=<url> */
所以在出现异常时,得到异常所处的文件之后,判断该文件内容中是否有上面的标记,就可以判断是否有 Source Map 文件。source-map-support 中就是这样进行判断。
如何获取到 Source Map 文件?
在第一个问题里面,知道了有 Source Map 文件,同时也就获取了文件所在位置,直接去请求就可以了。在规范中,推荐在响应头部返回 SourceMap
指向关联的 Source Map 文件,在最新 Chrome 中试了下,并不会默认带上的,可能需要自己手动设置。这个是示例页面。
如何解析 Source Map 文件?
理解规范里面编码的方式,就可以反向的进行解析。现有 source-map 库提供了解析的功能。这个是示例页面。
什么时候
在上面已经知道怎么去使用 source map 文件,那么该什么时候去进行这个过程?上面给出的示例,都是在前端进行处理,实际上服务器端也可以进行处理,这个时候需要考虑的因素有:
- 是否会造成影响主流程的运行,因为如果放在前端处理,JavaScript 是单线程,可能会产生影响。
- 是否能接受额外的请求,上面示例中就有请求 source map 文件,其大小一般都比压缩后源文件要大。
- 是否能接受源码泄漏的风险。
前端异常处理上报,总的来说是为了及时甚至提前发现问题并处理,为用户提供更好的体验,也为改进产品提供数据参考。从这个方便来看,异常处理上报是一个辅助的功能,应该尽量减少占有的资源,所以 source map 的解析建议放到服务器端处理。