关于webpack(v5.74.0)的source-map原理

source-map我相信大家平时接触的也比较多,但是它的具体实现原理可能比较模糊。接下来我带大家一步步解析source-map原理。

基础配置

module.exports = {
  entry: {
    index: './src/index.js',
  },
  mode: 'production',
  output: {
    clean: true
  },
  devtool: 'source-map'
}
// index.js
console.log('I', 'am', 'Chris');

打包后生成的文件:

// 打包后生成的文件index.js
console.log("I","am","Chris");
//# sourceMappingURL=testxx.js.map

// 打包后生成的文件index.js.map
{
  "version": 3,
  "file": "testxx.js",
  "mappings": "AAAAA,QAAQC,IAAI,IAAK,KAAM",
  "sources": [
    "webpack://test-webpack/./src/index.js"
  ],
  "sourcesContent": [
    "console.log('I', 'am', 'Chris');"
  ],
  "names": [
    "console",
    "log"
  ],
  "sourceRoot": ""
}

生成具体步骤如下:

每一行都代表一个连续的字段:
 1. 第一个字1表示生成的字段在第12. 第二个字0表示生成的字段在第03. 第三个字0表示生成的字段在sources文件中下标为0
 4. 第四个字1表示原始的字段在第15. 第五个字0表示原始的字段在第01|0|0|1|0
1|8|0|1|8
1|12|0|1|12
1|17|0|1|16
1|21|0|1|23

下面开始优化:
// 1. 去掉输出行,用;来表示换行
0|0|1|0
8|0|1|8
12|0|1|12
17|0|1|16
21|0|1|23
// 2. 加入映射 "names": [ "console", "log" ],names 是转换前的变量名。这样就可以通过下标来索引了,mapping 里面就不用保存变量名,只保留 names 的索引就行。
0|0|1|0|0
8|0|1|8|1
12|0|1|12|-1
16|0|1|17|-1
21|0|1|23|-1
// 3. 输出列、输出行、输入列、输入行、sources等做相对位置(后面的绝对位置-前面的绝对位置)
// 其中names中没有的用-1代替
0|0|0|0|0
8|0|0|8|1
4|0|0|4|-1
4|0|0|5|-1
5|0|0|6|-1
// 4. vlq编码然后转换为base64(如果6位中间的四位(不超过15)可以显示,那么只用6位,第一位表示是否有连接,最后一位表示正负)。如果names索引为负数那么不显示,只显示4位。
0|0|1|0|0  000000 000000 000000 000000 000000 AAAAA
8|0|1|8|1  010000 000000 000000 010000 000010 QAAQC
4|0|1|4|-1 001000 000000 000000 001000 IAAI
4|0|1|5|-1 001000 000000 000000 001010 IAAK
5|0|1|6|-1 001010 000000 000000 001100 KAAM
最后我们和webpack打包出来的对比下得出的是一样的。

base64图表:
在这里插入图片描述

下面我带大家看看webpack中源码实现,和我们上面说的是一致的。

// webpack-sources\lib\helpers\createMappingsSerializer.js
const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(
	""
);
const CONTINUATION_BIT = 0x20;
const createFullMappingsSerializer = () => {
	let currentLine = 1;
	let currentColumn = 0;
	let currentSourceIndex = 0;
	let currentOriginalLine = 1;
	let currentOriginalColumn = 0;
	let currentNameIndex = 0;
	let activeMapping = false;
	let activeName = false;
	let initial = true;
	return (
		generatedLine,
		generatedColumn,
		sourceIndex,
		originalLine,
		originalColumn,
		nameIndex
	) => {
		if (activeMapping && currentLine === generatedLine) {
			// A mapping is still active
			if (
				sourceIndex === currentSourceIndex &&
				originalLine === currentOriginalLine &&
				originalColumn === currentOriginalColumn &&
				!activeName &&
				nameIndex < 0
			) {
				// avoid repeating the same original mapping
				return "";
			}
		} else {
			// No mapping is active
			if (sourceIndex < 0) {
				// avoid writing unneccessary generated mappings
				return "";
			}
		}

		let str;// 当前行小于生成后的行添加;
		if (currentLine < generatedLine) {
			str = ";".repeat(generatedLine - currentLine);
			currentLine = generatedLine;
			currentColumn = 0;
			initial = false;
		} else if (initial) {
			str = "";
			initial = false;
		} else {
			str = ",";
		}

		const writeValue = value => {
			const sign = (value >>> 31) & 1;
			const mask = value >> 31;
			const absValue = (value + mask) ^ mask;
			let data = (absValue << 1) | sign;
			for (;;) {
				const sextet = data & 0x1f;
				data >>= 5;
				if (data === 0) {
					str += ALPHABET[sextet];
					break;
				} else {
					str += ALPHABET[sextet | CONTINUATION_BIT];
				}
			}
		};// 当前绝对列减去前一个绝对列获取相对列
		writeValue(generatedColumn - currentColumn);
		currentColumn = generatedColumn;
		if (sourceIndex >= 0) {
			activeMapping = true;// 设置文件索引
			if (sourceIndex === currentSourceIndex) {
				str += "A";
			} else {
				writeValue(sourceIndex - currentSourceIndex);
				currentSourceIndex = sourceIndex;
			}// 获取相对行
			writeValue(originalLine - currentOriginalLine);
			currentOriginalLine = originalLine;
			if (originalColumn === currentOriginalColumn) {
				str += "A";
			} else {// 设置相对列
				writeValue(originalColumn - currentOriginalColumn);
				currentOriginalColumn = originalColumn;
			}
			if (nameIndex >= 0) {// 如果name>=0设置相对name,否则不记录,此时为4个字母
				writeValue(nameIndex - currentNameIndex);
				currentNameIndex = nameIndex;
				activeName = true;
			} else {
				activeName = false;
			}
		} else {
			activeMapping = false;
		}
		return str;
	};
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Young soul2

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值