Source Map

标准

Source Map 第三版提案open in new window

结构

{
    "version" : 3,
    "file": "out.js",
    "sourceRoot": "",
    "sources": ["foo.js", "bar.js"],
    "sourcesContent": [null, null],
    "names": ["src", "maps", "are", "fun"],
    "mappings": "AAgBC,SAAQ,CAAEA"
}
1
2
3
4
5
6
7
8
9
  • version:Source Map 的版本,目前为3
  • file:转换后的文件名。 -sourceRoot:转换前的文件所在的目录。如果与转换前的文件在同一目录,该项为空。 -sources:转换前的文件。该项是一个数组,表示可能存在多个文件合并。 -names:转换前的所有变量名和属性名。 -mappings:记录位置信息的字符串。

mappings属性的内容是这样的:

mappings: "AAAAA,BBBBB;CCCCC"
1

就表示,转换后的源码分成两行,第一行有两个位置,第二行有一个位置。

每个位置使用五位,表示五个字段。

从左边算起,

  • 第一位,表示这个位置在(转换后的代码的)的第几列。
  • 第二位,表示这个位置属于sources属性中的哪一个文件。
  • 第三位,表示这个位置属于转换前代码的第几行。
  • 第四位,表示这个位置属于转换前代码的第几列。
  • 第五位,表示这个位置属于names属性中的哪一个变量。

Base VLQ 编码是如何让 Source Map 文件尽量小的?

  • 通过;来分隔每一行,因此可以省略打包后文件的行号
  • 通过第二位指明是sources里的哪一个文件,通过第五位指名是names里的哪一个变量
  • 使用相对偏移,将原先较大的数字变为较小的数字

更多内容可参考:

Q: 每个 Base64 的字符是 6 比特,代表一个 VLQ 的二进制组(binary group),在这个 6 比特的二进制组里第一位是连续的指示符(0为不连续,1为连续),最后一位是符号位(0为正,1为负),只有剩下的 4 位表示一个数字,这意味着 Source Map 里的最大偏移量为 16 ?

A: 不会,若偏移超过 16,则会用 2 个及以上二进制组来表示,因此一个偏移量可能会产生多个 Base64 字符。因此mappings里的每个位置上的字符数可能不止 5 个,但是其所代表的信息最多 5 个,也就是说,里面的多个字符可能代表 1 个信息。

实例:

program.ts:

const aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 1;
1

经过 TypeScript Compiler 编译后,产生program.jsprogram.js.map

program.js:

"use strict";
var aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 1;
//# sourceMappingURL=program.js.map
1
2
3

program.js.map:

{
    "version":3,
    "file":"program.js",
    "sourceRoot":"",
    "sources":["../src/program.ts"],
    "names":[],
    "mappings":";AAAA,IAAM,oCAAoC,GAAG,CAAC,CAAC"
}
1
2
3
4
5
6
7
8

其中的mappings经过Base64 VLQ 解码open in new window后,得到:

1) [0,0,0,0], [4,0,0,6], [36,0,0,36], [3,0,0,3], [1,0,0,1], [1,0,0,1]
1

注意,上面的数字都是表示偏移。

Format: (([from_position](source_index)=>[to_position]))
([0,0](#0)=>[1,0]) | ([0,6](#0)=>[1,4]) | ([0,42](#0)=>[1,40]) | ([0,45](#0)=>[1,43]) | ([0,46](#0)=>[1,44]) | ([0,47](#0)=>[1,45])
1
2

可以发现,oCAAoC里的第一个oC只代表第一位,即转换后的列号,oC是对36的编码;第二个oC是转换前的列号。

前提知识

疑问

Source Map 文件是否影响网页性能?

不会影响。Source Map 只有在打开 DevTools 的情况下才会开始下载,而绝大部分用户不会打开 DevTools,因此不会有影响。

此外,Source Map 文件的请求不会显示在 Network 里(因为浏览器隐藏了),若是通过抓包工具,就能看到 Source Map 文件的请求。

浏览器如何知道源文件与 Source Map 文件的映射关系?

方式一

// 打包后的代码..
//# sourceMappingURL=bundle.js.map

// 或者
//# sourceMappingURL=http://example.com/path/to/your/bundle.js.map

// 或者
//# sourceMappingURL=base64 编码的文件内容
1
2
3
4
5
6
7
8

打包后的bunlde.js文件的末尾,会存在一行注释,指明该文件对应的 Source Map 文件的地址。详见:MDN - Use a source mapopen in new window

方式二

在请求打包文件bundle.js的响应里,添加一个响应头:

SourceMap: http://example.com/path/to/your/bundle.js.map
1

SourceMap: bundle.js.map
1