目录
1 Source Maps的意义
当我们使用webpack打包源代码的时候,可能很难追踪到原始的报错位置。比如:我们把多个js源文件打包到一个bundle.js里,当其中某一个源文件出错时,浏览器的报错只会指向bundle.js,这样很不利于代码调试。
Webpack中的Source maps,正是为了解决这个问题。
2 Source Maps的结构
简单来说,Source maps就是一个保存着位置信息的文件。它可以把已编译代码映射到原始源代码,从而让我们可以准确地获知是哪个源文件出错。
Source maps文件中包含一个JSON对象,这个对象存储着映射信息,格式如下:
{
version: 3,
file: "script.js.map",
sources: [
"app.js",
"content.js",
"widget.js"
],
sourceRoot: "/",
names: ["slideUp", "slideDown", "save"],
mappings: "AAA0B,kBAAhBA,QAAOC,SACjBD,OAAOC,OAAO..."
}
各个字段的含义:
version:Source maps的版本
file:Source maps的文件名
sources:原始文件的URL
sourceRoot:(可选项)原始文件所在的目录
names:原始文件中所有变量和函数名
mappings:包含实际代码映射的Base64 VLQ字符串。
3 位置映射
我们试想一种方案——可以用一个字段来保存映射关系,在这个字段里面有六个位置,分别代表:
输出代码的所在行
输出代码的所在列
输入代码的所在文件的名称
输入代码的所在行
输入代码的所在列
代码的内容(比如函数名、变量名。但是,这个位置如果没有可以对应的内容的时候,这种情况下可以省略)
我们来看一个例子:a.js为原始文件(输入文件),而b.js为处理后的文件(输出文件)。
(注:图片来自文末资料4)
其中the这个字符串,用前面的映射方式,就可以记录为:
我们可以做一些优化,把输入文件名存储到JSON对象的sources字段中,把字符组合存储到names字段中。
优化后:
在实际的Source maps中,做了更近一步的优化:
省略输出代码的所在行,直接用mappings字段中的分号(;),来区分每一行。
除了第一个代码使用绝对位置之外,后面的代码都是用相对位置来记录。这样可以避免行/列号过大的问题。
最终优化的结果:
但我们可以看到,实际的mappings字段值似乎跟这种记录映射的方式有些不同。原因在于,mappings还将这种映射进行了Base64 VLQ编码。
4 Base64-VLQ编码
以映射关系[10|0|0|0|0]为例,在这里我们用|划分每个数。但使用这种方法的话,其中的|需要占一定的字符。于是,连续位的概念被提出了,用来解决这个问题。
VLQ(Variable-length quantity)编码可变长,它使用六个二进制位表示一个字符,其中:
第一位:连续位。如果是1,则代表后面的6个二进制位跟这里属于同一个数,如果是0,则代表这个数到这里结束了。
最后一位:符号位。0代表正数,1代表负数。
中间的四位:用来表示具体的数值。因此,它可以表示-15~15范围内的数字。如果数字超过这个范围,就需要多几个字符来表示。
那么,VLQ具体是如何编码的呢?
我们以数值16为例,演示VLQ编码:
将16改写成二进制形式10000。
在最右边补充符号位。因为16大于0,所以符号位为0,整个数变成100000。
从右边的最低位开始,将整个数每隔5位,进行分段,即变成1和00000两段。如果最高位所在的段不足5位,则前面补0,因此两段变成00001和00000。
将两段的顺序倒过来,即00000和00001。
在每一段的最前面添加一个"连续位",除了最后一段为0,其他都为1,即变成100000和000001。
将每一段转成Base64编码。
Base 64对应表:
比如位置映射如下:
[0|0|0|0 , 9|0|0|9|0 , 9|0|0|9|1 , 6|0|1|-14|1 , 8|0|0|8|1 , 4|0|0|4 , 9|0|0|10|-2]
则Base64-VLQ编码之后为:
"mappings":"AAAA,SAASA,SAASC,MACdC,QAAQC,IAAI,SAAUF"
5 Source Map的开启方式
在Chrome和Firefox的开发者工具中都内置了对Source maps的支持。
下图为Chrome开发者工具的对应截图:
让我们来看一下如何启用Source Maps。
第一种方式,可以通过在优化后的文件尾部添加特殊的注释,格式如下:
//# sourceMappingURL=/path/to/script.js.map
第二种方式,可以通过HTTP header中的X-SourceMap,格式如下:
SourceMap: /path/to/script.js.map
6 Webpack中的Source Maps
在Webpack中配置Source Map有两种方式:
Devtool
Plugin:SourceMapDevToolPlugin/EvalSourceMapDevToolPlugin
二者不可混用,只能择一。
Webpack中提供了多种Source Maps的可选值,在webpack/examples/source-map/显示了每种Source Maps的案例。
那么我们应当如何选择呢?
对于开发模式,相比打包的体积大小,我们更加注重打包速度。而在生产模式下,我们则更在意准确性和打包体积。
7 归纳总结
为了方便对打包后的代码进行调试,Source Maps提供了一种代码位置映射机制。
位置映射策略和Base64-VLQ编码,二者在Source Maps中起到了关键作用。