不可或缺的sourceMap

289 阅读4分钟

sourceMap的作用

引用一下简介

这是一种将组合/缩小文件映射回未构建状态的方法。当您为生产而构建时,连同压缩和组合 JavaScript 文件,您会生成一个包含原始文件信息的源映射。当您在生成的 JavaScript 中查询某个行号和列号时,您可以在返回原始位置的源映射中进行查找。开发人员工具(目前是 WebKit nightly builds、Google Chrome 或 Firefox 23+)可以自动解析源映射并使其看起来好像您正在运行未压缩和未组合的文件。

如何构建呢?

可以通过 webpack 来打包并构建一个 sourceMap

// index.js
var a = 1;
console.log(a)
console.log(b)
// webpack.config.js
const path = require('path');
module.exports = {
  entry: "./scr/index.js",
  mode: 'development',
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist'),
    clean: true
  },
  devtool: 'source-map',
}

执行命令 npx webpack 得到 main.js 和 main.js.map。并直接在 index.html 文件中引入 main.js 不管 main.js.map

可以看到浏览器能够正常的报出错误,指明了错误的位置(文件和报错位置)。很奇怪没有引入其他的文件但是就是能够看到 但是通过学习可以知道问题在这一行

/******/ (() => { // webpackBootstrap
var __webpack_exports__ = {};
/*!**********************!*\
  !*** ./scr/index.js ***!
  **********************/
var a = 1;
console.log(a)
console.log(b)
/******/ })()
;
​
//# sourceMappingURL=main.js.map

如果我取消掉这一样或者是改变这一行的信息就不会在显示问题的真正位置,可以了解到浏览器的调试工具是通过这一句注释来获得 sourceMap 文件的信息的 我们在来看一下这个文件

//main.js.map
{
  "version": 3,
  "file": "main.js",
  "mappings": ";;;;;AAAA;AACA;AACA,c",
  "sources": [
    "webpack://webpack-demo-two/index.js"
  ],
  "sourcesContent": [
    "var a = 1;\r\nconsole.log(a)\r\nconsole.log(b)"
  ],
  "names": [],
  "sourceRoot": ""
}

重点在这一段上sourcesContent里面存储着文件的源代码如果我去修改这里面的东西我们在看一下抛错的信息是否能对应上

//main.js.map
{
    //...
    "sourcesContent": [
        "var c = 1;\r\nconsole.log(w)\r\nconsole.log(c)"
    ]
}

那么到这里就可以明白 调试工具是如何映射源码抛错位置的了

重点在学习一下 "mappings": ";;;;;AAAA;AACA;AACA,c" 代表了什么意思,这里面表明源码对应的位置信息。mappings的信息 ; 代表 , 代表对应转换后源码的一个位置第一个逗号代表该行的第一个位置,字母是以VLQ编码表示的代表对应转换之前的源码位置,每个位置会使用五位,每个位置表示五个字段

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

第五位不是必需的,如果该位置没有对应names属性中的变量,可以省略第五位。

VLQ编码是变长的。如果(整)数值在-15到+15之间(含两个端点),用一个字符表示;超出这个范围,就需要用多个字符表示。它规定,每个字符使用6个两进制位,正好可以借用Base 64编码的字符表。

img

如果超过了15比如16就会使用两个VLQ编码 也就是gB

VLQ 是 Variable-length quantity 的缩写,是一种通用的,使用任意位数的二进制来表示一个任意大的数字的一种编码方式。

VLQ 是先将数字转换成二进制树,然后按照7bit一组来分割,不够的补0。然后会将这两个分成最低位和最高位。最低为的七位二进制的最高位补0表示最后一位,最高位的二进制的最高位补1表示没有结束。然后再由最高位和最低为依次拼接在一起。

而Base64 VLQ 受限于 BASE64采用的字符集,一个BASE只能表示6bit的数据由于后面还有补位的树所以5bit一组进行分割成两组,不够的补0。先取出高位组然后再最高位补1表示还有数据,再取出低位组高位补0然后再由低位+高位的方式拼起来

Base64 VLQ 还要能表示负数所以最后一位0代表正数1代表负数(这一步要在分割前进行

总结: VLQ 中,编码顺序是从高位到低位,在 Base64 VLQ 中,编码顺序是从低位到高位。

所以可以得出 sourceMap 是由base64VLQ编码的而不是VLQ。

为什么会这样呢就是版本经行了更替 IDuxFE的介绍

2011年,第三代即Source Map Revision 3 Proposal出炉了,这也是咱们现在运用的 Source Map 版别。从文档的命名看来,此刻的 Source Map 已脱离 Clousre Compiler ,演化成了一款独立东西,也得到了浏览器的支撑。这一版相较于二代最大的改动是 mapping 算法的紧缩换代,运用VLQ编码生成base64前的 mapping ,大大缩小了 .map 文件的体积。

参考文章

JavaScript Source Map 详解

BASE64 VLQ 编码规则

BASE64 VLQ 在线转换