什么是SourceMap?webpack devtool超详细解析
你是否有过在控制台点击报错信息,却看到一堆混淆代码头晕眼花?你是否遇到即使定位到了报错代码,发现跟源码所在的行还不一样?
如果你也有上述苦恼,那就跟着本文花几分钟时间来了解与解决一下给你带来苦难的根源吧~
什么是SourceMap
在Web项目越来越庞大的今天,我们为了项目的性能和应对不同浏览器的兼容性,往往都会对需要上线的代码进行处理。比如对代码进行压缩混淆、ES6语法转ES5语法、多个文件合并与提取公共代码等。
这样一顿操作下来的结果就是我们代码的性能和兼容性虽然得到了提高,但也带来了调试上的困难。为了方便我们在线上环境或开发环境调试代码,SourceMap出现了。
SourceMap 是一个记录着源代码与转换后代码位置对应关系的文件,有了这个文件,我们就能通过点击控制台的报错信息直接跳转定位到对应源代码的报错位置(取决于相应配置)。
使用 SourceMap
要想给打包后的代码引入 SourceMap,只需要配置 webpack 的 devtool
选项即可,devtool
就是控制是否生成,以及如何生成 SourceMap 的。
官方给出的 devtool
配置项多达25种,但无需害怕,我们只用了解其中2-3种就能满足我们的大部分使用场景。即使仍不能满足,但只要我们掌握 devtool
的组合方式,不管多麻烦的场景都不在话下。
下面让我们来具体了解一下吧!
不使用 SourceMap
作为一组对照试验,没有对比就没有伤害,让我们先来看看不使用 SourceMap 的情况下(devtool: false
),我们点击控制台报错会看到什么。
我们会看到控制台的报错变成了我们在配置文件中定义的 bundle.js
,点击进去发现我们的代码和 webpack 的代码糅在了一块,因此报错代码的位置也发生了改变,更可怕的是,当我们开启了压缩混淆,我们想要读懂报错位置会变得更加困难!
这太糟糕了!SourceMap 没你真不行!
生成 SourceMap
我们给 devtool
配置一下
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist'),
},
devtool: 'cheap-source-map',
};
然后我们运行webpack,重新打开控制台查看报错
很完美不是吗,但 cheap-source-map
代表什么意思呢?而且官方文档怎么会有25种那么多呢?
接下来让我们一步一步了解个中奥秘,以及如何组合他们。并且,在本文结尾,我们将列出几个日常开发中常用的且推荐的配置。
devtool 选项
devtool | performance | production | quality | comment |
---|---|---|---|---|
(none) | build: fastest rebuild: fastest | yes | bundle | Recommended choice for production builds with maximum performance. |
eval | build: fast rebuild: fastest | no | generated | Recommended choice for development builds with maximum performance. |
eval-cheap-source-map | build: ok rebuild: fast | no | transformed | Tradeoff choice for development builds. |
eval-cheap-module-source-map | build: slow rebuild: fast | no | original lines | Tradeoff choice for development builds. |
eval-source-map | build: slowest rebuild: ok | no | original | Recommended choice for development builds with high quality SourceMaps. |
cheap-source-map | build: ok rebuild: slow | no | transformed | |
cheap-module-source-map | build: slow rebuild: slow | no | original lines | |
source-map | build: slowest rebuild: slowest | yes | original | Recommended choice for production builds with high quality SourceMaps. |
inline-cheap-source-map | build: ok rebuild: slow | no | transformed | |
inline-cheap-module-source-map | build: slow rebuild: slow | no | original lines | |
inline-source-map | build: slowest rebuild: slowest | no | original | Possible choice when publishing a single file |
eval-nosources-cheap-source-map | build: ok rebuild: fast | no | transformed | source code not included |
eval-nosources-cheap-module-source-map | build: slow rebuild: fast | no | original lines | source code not included |
eval-nosources-source-map | build: slowest rebuild: ok | no | original | source code not included |
inline-nosources-cheap-source-map | build: ok rebuild: slow | no | transformed | source code not included |
inline-nosources-cheap-module-source-map | build: slow rebuild: slow | no | original lines | source code not included |
inline-nosources-source-map | build: slowest rebuild: slowest | no | original | source code not included |
nosources-cheap-source-map | build: ok rebuild: slow | no | transformed | source code not included |
nosources-cheap-module-source-map | build: slow rebuild: slow | no | original lines | source code not included |
nosources-source-map | build: slowest rebuild: slowest | yes | original | source code not included |
hidden-nosources-cheap-source-map | build: ok rebuild: slow | no | transformed | no reference, source code not included |
hidden-nosources-cheap-module-source-map | build: slow rebuild: slow | no | original lines | no reference, source code not included |
hidden-nosources-source-map | build: slowest rebuild: slowest | yes | original | no reference, source code not included |
hidden-cheap-source-map | build: ok rebuild: slow | no | transformed | no reference |
hidden-cheap-module-source-map | build: slow rebuild: slow | no | original lines | no reference |
hidden-source-map | build: slowest rebuild: slowest | yes | original | no reference. Possible choice when using SourceMap only for error reporting purposes. |
组合方式
通过阅读 devtool
的配置项,我们可以提取出一些相通的东西。
eval、inline、hidden、nosources、cheap。
这几个其实就是一种组合关系,他们的组合方程式如下
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
其中:
inline-|hidden-|eval
:三个值时三选一;cheap
可选值,并且可以跟随module
的值;nosources
:可选值。
具体都有什么作用,我们接着往下看。
inline
inline
会将 SourceMap 内联到原始文件中,不会生成额外的 【source map】 文件。
什么意思呢?比如我使用cheap-source-map
进行打包。可以看到 dist
目录下有这么一个文件
这个 .map
文件对应着每一个打包后的 js
文件,用来与相对应的文件进行代码定位与匹配。
每一个.map
文件都有一套格式,比如这个bundle.js.map
文件就写着一套标准【SourceMap】格式。
它包含的意思如下:
- file: 该 SourceMap 对应的文件的名称
- mappings: 压缩混淆后的代码定位源代码的位置信息
- names: 源文件中的变量名
- sourceRoot: 源文件根目录,这个值会加在每个源文件之前
- sourcesContent: 源代码字符串列表,用于调试时展示源文件,列表每一项对应于sources
- sources: 源文件列表
- version: SourceMap的版本
而相应的被 【SourceMap】 对应的文件下,结尾也会标上相应的信息# sourceMappingURL=xxx.js.map
,比如这个例子中的bundle.js
标记如下:
浏览器在加载bundle.js
时,会通过这个标记找到对应的 .map
文件,然后加载其中的 sources
字段,在控制台中生成对应的目录结构,再把 sourcesContent
中的内容按顺序写入相应的生成的目录文件中。而如果我们使用inline
,那么标记后的bundle.js.map
则会直接被 【base64】 编码,然后拼接到后面去。
eval
使用 eval
生成 SourceMap,也不会生成额外的 .map
文件,因为 eval
中为字符串形式,所以当源码变动的时候进行字符串处理会提升 rebuild 的速度。
虽然重新编译的速度很快,但 eval
也是有缺点的,比如容易遭到 XSS 攻击,且在旧的浏览器下性能会很不理想。
在现代浏览器中有两种编译模式:fast path 和 slow path。fast path 是编译那些稳定和可预测(stable and predictable)的代码。而明显的,eval 不可预测,所以将会使用 slow path。在旧的浏览器中,使用 eval 的性能会大幅下降。
所以,eval
一般只用于开发环境,不会用于打包线上环境的代码。
hidden
hidden
会生成 .map
文件,但不会在编译后的代码内添加上面提到的那个标记,因此点击报错信息在控制台中是看不到源码的,在开发场景下,使用调试控制台查看报错的信息,效果等同于devtool: false
。
但这种可以用于错误收集等场景,出错时前端把出错的行列传给服务端,服务端根据行列以及.map
文件解析出出错的源码位置。
cheap
cheap
可以单用,也可以拼接 module
(即cheap-module
)使用。
cheap
会忽略列信息,【SourceMap】只有行映射,可以加快打包速度。需要注意的是,moudle
一定是跟 cheap
一起使用的, 表示所映射的阶段, 如果没有 module
映射的是 loader 处理后的代码信息,如果加了 module
那就是 loader 处理前的源码。
nosources
使用nosources
可以使得【sourceMap】中不带源码,即没有sourcesContent
的内容,但仍会有准确的错误行列信息,可以避免源码泄露。
常用配置推荐
开发环境推荐使用
- eval-cheap-source-map
- eval-cheap-module-source-map
生产环境推荐使用
- hidden-source-map
- nosources-source-map
总结
SourceMap 是一个记录着源代码与转换后代码位置对应关系的文件,有了这个文件,当代码出现异常报错的时候,我们就可以准确定位到具体的位置,同时在某些配置下,我们还能在浏览器控制台直接查看报错的源代码。
在 webpack 中使用 SourceMap 的关键是使用 devtool
,而想要用好 devtool
就要弄明白eval、inline、hidden、nosources、cheap 这些关键字的含义,剩下的不过是如同拼积木一般的【组装】罢了。
转载自:https://juejin.cn/post/7245536612628971581