Source Map
arminzheng Lv2

简述

通常 js 脚本 在通过诸如 webpack 等打包压缩后,会变成仅一行的 js 文件,这样带来几点好处:

  1. 压缩,减少体积(jquery.min.js 能压缩十倍文件大小)
  2. 多个文件合并成一个,能在第一次访问时候减少 js 文件请求数
  3. 将其他脚本编译成 JavaScript 脚本供页面使用

但同时也带了一个新的问题:我们的 debug,诸如 console.log 和 报错信息 等,控制台无法输出正确的源码位置。由此 Source Map 应运而生。

Source Map 就是 源码 和对应 压缩成仅一行后的 js 代码 的对照文件

源码转换

  1. 压缩后:jquery.min.js 例子

  2. Source Map:jquery.min.map 例子

可以看到 如1所示 压缩后的 jquery.min.js 文件有两行:一行 js 脚本 + 一行注释

其中第二行注释内容:

1
//@ sourceMappingURL=jquery.min.map

这是一种固定格式:告诉我们将 URL 链接中的 jquery.min.js 替换为 jquery.min.map,即可得到 Source Map 文件。这不关是告诉我们,同时也是告诉浏览器如何输出正确的源码位置

Source Map 文件格式

固定由一个对象组成,里面包含 6 个属性(5 个必填)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
// 版本
version : 3,
// 转换后的文件名
file: "out.js",
// 转换前的文件所在的目录。如果与转换前的文件在同一目录,该项为空或忽悠
sourceRoot : "",
// 转换前的文件。该项是一个数组,表示可能存在多个文件合并
sources: ["foo.js", "bar.js"],
// 转换前的所有变量名和属性名
names: ["src", "maps", "are", "fun"],
// 记录位置信息的字符串,也是实际上进行转换的映射信息
mappings: "AAgBC,SAAQ,CAAEA"
}

Source Map 对象的 mappings 属性

mappings 属性仅包含一段字符串。里面的内容需要进行层次划分来达到,横纵坐标转换。

1
2
3
4
{
...
mappings: "AAgBC,SAAQ,CAAEA"
}

分隔符有:(;)分号(,)逗号(总共两种)

第一次分割是用(;)分号,对应压缩后的行数(压缩目的是节省,所以压缩后大多为一行,也会出现多行的情况,但总是从第一行开始,前面不会有空行)第一行的内容结束,分号隔开,接上第二行

第二次分割使用(,)逗号,对应压缩后的位置。通常一个报错信息会指出一个错误开始的地方,开始的地方到语句结束的地方就叫一个位置。(注意:这一层不表示压缩后的对应列数,仅大概位置)

最后分割为一段字符串单位后,就进行位置转换了。

AAgBC 这样的字符串,是按照 VLQ 编码(Variable-length Quantity)表示的,表示该位置对应的转换前的源码位置。

VLQ 编码

VLQ 编码字段通常有 5 个字符(4 个字符、6 个字符)

  • 从左边算起:
    1. 第一位表示这个位置在压缩后的代码的第几列
    2. 第二位表示这个位置属于 sources属性 中的哪一个文件
    3. 第三位表示这个位置属于转换前代码的第几行
    4. 第四位表示这个位置属于转换前代码的第几列
    5. 第五位表示这个位置属于 names属性中的哪一个变量

由于每个字符使用 6 个两进制位,所以字段内容的对应值与 Base64 相似,具体的对应参数可以查阅 VLQ 编码