我在开发一个json转化成ts类型的功能时,用到了一个json2ts的库。本着看源码学原理的精神,我打开了源码,第一眼就看到了一个.map为后缀的文件。
作为一个前端小白,我并不知道.map文件是啥,所以我就开始了我的谷歌搜索之旅,也就是我们本文的主题——Source map。
首先,不急着讲Source map是什么,我们先了解一下背景。
如今,大部分源码都需要经过转换才能投入生产环境,如早期的jquery,现在流行的框架vue、react等。
而源码转换后,都使得实际运行的代码不同于开发代码,在debug时就变得几乎无法看懂。转换后的代码在报错定位时,一行可能有成千上万个字符,变量名可能被修改,你完全无法看出代码出了什么问题。那咋整呢?
这就是Source map要解决的问题。
Source map,用官方点的话来说,是保存源代码映射关系的文件。简单来说,他就是一个信息文件,他是一个独立的map文件,里面储存着位置信息。也就是说,转换后的代码的每一个位置,所对应的转换前的位置。
有了它,出错的时候,除错工具将直接显示原始代码,而不是转换后的代码。
康康例子,对比一下使用前和使用后(借用了阮一峰老师的图片):
如何启用Source map
只要在转换后的代码尾部,加上一行就可以了。以json2ts源码为例:
//# sourceMappingURL=json2ts.js.map
然后在chrome浏览器的开发者工具 -> 设置中,把Enable JavaScript source maps钩上就完事了。
如何生成Source map
Google的Closure编译器
我进入他的官网,用Google翻译给他翻译了一下(英文水平属实有限),让我们来看看他的定义:
看到这,我感觉这玩意有点牛的啊,有种eslint加强版的感觉,不仅能检查语法,还能优化你的代码。
到这我又有了新的疑问:
这么厉害的东西,项目应该很常用吧?是不是可以用webpack插件引入呢?
这个问题我文章后头会提到,现在我们先继续看看这个Source map。
Source map的格式
我们再看看Source map文件里头的样子(举个简单例子):
version : 3,
file: "out.js",
sourceRoot : "",
sources: ["foo.js", "bar.js"],
names: ["src", "maps", "are", "fun"],
mappings: "AAgBC,SAAQ,CAAEA"
这个文件是一个js对象,可以被解释器读取。我们来看看他各个属性的意思:
version:Source map的版本,此处为3。
file:转换后的文件名。
sourceRoot:转换前的文件所在的目录。如果与转换前的文件在同一目录,该项为空。
sources:转换前的文件。该项是一个数组,表示可能存在多个文件合并。
names:转换前的所有变量名和属性名。
mappings:记录位置信息的字符串。
在这我们简单讲一讲mapping属性,他的作用是让两个文件的各个位置一一对应。
用分号(;)表示行对应,每个分号对应转换后源码的一行。
用逗号(,)表示位置对应,每个逗号对应转换后源码的一个位置。
用VLQ编码表示位置转换,代表该位置对应的转换前的源码位置。
举个例子:
mappings:"AAAAA,BBBBB;CCCCC"
表示:转换后的源码分成两行,第一行有两个位置,第二行有一个位置。
关于位置对应的原理和VLQ源码,我在这就不详细说明了,感兴趣的同学可以去看看阮一峰老师的JavaScript Source Map 详解。
接下来,让我们回过头来看看前面提到的两个疑问:
webpack是否有关于source map的配置?
关于优化代码,他是怎么实现的呢?
webpack的source map
webpack是有关于source map的设置的,也就是配置devtool。
最简单的配置,就是:
devtool: 'source-map'
当然你也可以使用 SourceMapDevToolPlugin
进行更细粒度的配置。查看 source-map-loader
来处理已有的 source map。
常见配置信息如下:
eval: 生成代码 每个模块都被eval执行,并且存在@sourceURL
cheap-eval-source-map: 转换代码(行内) 每个模块被eval执行,并且sourcemap作为eval的一个dataurl
cheap-module-eval-source-map: 原始代码(只有行内) 同样道理,但是更高的质量和更低的性能
eval-source-map: 原始代码 同样道理,但是最高的质量和最低的性能
cheap-source-map: 转换代码(行内) 生成的sourcemap没有列映射,从loaders生成的sourcemap没有被使用
cheap-module-source-map: 原始代码(只有行内) 与上面一样除了每行特点的从loader中进行映射
source-map: 原始代码 最好的sourcemap质量有完整的结果,但是会很慢。