webpack——sourcemap原理及其应用

1,505 阅读5分钟

学习一个东西要知道它为什么会出现,我觉得这是很重要的,所以先简单提一嘴。webpack作为现在前端工程化最热门的打包工具之一,能够帮助我们快速地打包和编译浏览器不认识的 web 资源,在webpack的默认配置下打包过的代码是经过压缩丑化的,在代码量较少时还可以进行调试,但当代码量增加时调试难度就会level up up up updevtool配置就是帮我们解决这个问题的。通过在devtool中配置相关的source-map风格就可以大大降低我们的调试难度。注意,本文有三个关键名词即sourcemap风格sourcemapdevtool

首先简单解释一下三者的关系,sourcemap文件是sourcemap风格的一种,然后devtool可以选择应用哪种sourcemap风格

对于devtool官方解释为

此项用于控制如何生成source map(也就是生成何种风格的source map)

对于source mapMDN解释为

source map 是从已转换的代码映射到原始源的文件,使浏览器能够重构原始源并在调试器中显示重建的原始源。

下面将source map拆分来讲解,即:如何生成source map,浏览器是怎么通过source map来对应到源代码的,一些常见的source map配置

如何生成source map

比较简单不做赘述,在webpack配置文件中进行如下配置

module.exports = {
    //其它省略
    devtool: "source-map"//这是风格最普通的sourcemap还可以设置为cheap-source-map等等
}

即可在打包出的文件中找到map文件。这里如果map文件为一行不便于阅读的话可以选中并按shift+alt+f对代码进行一个美化。在阅读的同时可以将mode配置更改为development模式来观察打包后的代码以及map文件。

浏览器如何通过sourcemap来对应到源代码的

devtool配置为source-map这个风格时会生成map文件并且打包生成的bundle.js的最后一行会有这样一段代码

//# sourceMappingURL=bundle.js.map

浏览器便是靠这段代码找到对应的map文件,对于一段console.log("1"),map文件的内容为:

{
    "version": 3,
    "file": "bundle.js",
    "mappings": "AAAAA,QAAQC,IAAI",
    "sources": ["webpack://webpack-note/./src/index.js"],
    "sourcesContent": ["console.log(\"1\")"],
    "names": ["console", "log"],
    "sourceRoot": ""
}
  • version 即为sorce-map版本,1.0版本生成的map文件为源文件的10倍大小,到3.0版本优化到了2.5倍大小。
  • file 打包后的代码(与源文件关联的生成文件名)
  • mappings 包含实际映射的 base64 VLQ 字符串,里面的信息一般有位置信息等、比如例子中console在源文件的第几行第几列,第几个源文件,在压缩文件的第几行,对应names数组中的哪个值这五个信息。(这里我觉得可以不用深究,反正只要知道五个字母分别代表了不同的信息即可)
  • sources 原始源文件的url数组
  • sourcesContent 原始源文件的实际内容
  • names 转换前的变量和属性名称
  • sourceRoot 所有源文件对应的根目录 浏览器通过这样一个map文件并且结合AST(抽象语法树)就可以知道打包后的各个代码在源代码的哪一个位置了。 github.com/mozilla/sou… 也可以自己去看source-map的官方解释,如果发现有讲的不对的地方希望回来指正。

下面说一说常用的sourcemap风格以及各种风格的特性

cheap-source-map与cheap-module-source-map

cheap-source-map风格即生成低开销的sourcemap,它不会生成列映射所以生成速度更快,在实际开发中,一般我们只用知道是哪一行错误了就可以定位到错误,所以这个sourcemap风格是一个不错的选择。

{
    "version": 3,
    "file": "bundle.js",
    "mappings": ";;;;;AAAA",
    "sources": ["webpack://webpack-note/./src/index.js"],
    "sourcesContent": ["console.log(\"1\");"],
    "names": [],
    "sourceRoot": ""
}

这个低开销在map文件中的mapings可以体现,普通sourcemap中mapings的每一个字符串有5位而低开销中只有4位就是缺少了源代码的列信息。

cheap-module-source-mapcheap-source-map的区别就在于前者对于对源自loader的sourcemap处理会更好。举个例子就是后者如果处理babel编译后的代码,那么行号等信息可以无法正确对应上,也就是说虽然报错时具体哪一句是会报出但行号会有一些偏差,而后者就可以避免这些错误。

eval、eval-source-map和inline-source-map

这前两种sourcemap风格虽然名字相似,但生成的内容有比较大的不同。

eval风格不会生成sourcemap文件可以视为一钟特殊的source-map,最后浏览器是通过打包后的代码中的eval函数后的baseURL来找到对应的源文件,这种方式是生成速度最快的sourcemap风格。但是不生成sourcemap文件意味着无法准确的对应行号,所以报错信息方面没有这么准确。 一个例子:

eval("console.log(\"1\");\n\n//# sourceURL=webpack://webpack-note/./src/index.js?");

eval-source-mapinline-source-map这两个风格都可以生成sourcemap但是都没有单独放在map文件中,前者的sourcemap文件内容放在eval的后面,后者的sourcemap文件内容放在打包代码的最后。

vue和react框架脚手架默认的sourcemap风格

vue-cli在开发环境中为source-map而在生产环境中为false或者source-map(具体要在config的index.js之中调整对应的参数)

react脚手架在开发环境中默认的是cheap-module-source-map在生产环境默认的似乎是缺省值或者false大家可以自行去查阅。

最后

很久没写文章了,一直在思考我自己存在的问题,过段时间会考虑混一混年终总结😄