关于webpack中sourcemap的文章不少,但感受大部分是翻译官方文档的说明, 缺少直观的用例,写这篇博客的目的是帮本身厘清这个概念, 也顺便将我本身收集的这方面的干货放在这。本文将尝试先讲清楚webpack中的sourcemap配置项的概念。javascript
sourcemap是为了解决开发代码与实际运行代码不一致时帮助咱们debug到原始开发代码的技术。尤为是现在前端开发中大部分的代码都通过编译,打包等工程化转换。好比开发环境下用scss写样式, 想在浏览器中在线编辑css那样编辑scss就不是那么容易了。从我本身看过的资料中, sourcemap的概念最先出如今12年, jquer1.9是较早支持sourcemap的库。这篇博客比较有表明性:Introduction to JavaScript Source Maps,阮一峰的文章JavaScript Source Map 详解也大量参考该博客。关于sourcemap的原理及做用,基本在这两篇文章中讲清楚了。回到webpack中的sourcemap,就我这几天的琢磨, 这方面资料相对比较零散,但凡搜索Webpack中sourcemap的配置, 老是能获得千篇一概的以下信息:
Sourcemap type Quality Notescss
eval: 生成代码 每一个模块都被eval执行,而且存在@sourceURLhtml
cheap-eval-source-map: 转换代码(行内) 每一个模块被eval执行,而且sourcemap做为eval的一个dataurl前端
cheap-module-eval-source-map: 原始代码(只有行内) 一样道理,可是更高的质量和更低的性能html5
eval-source-map: 原始代码 一样道理,可是最高的质量和最低的性能java
cheap-source-map: 转换代码(行内) 生成的sourcemap没有列映射,从loaders生成的sourcemap没有被使用webpack
cheap-module-source-map: 原始代码(只有行内) 与上面同样除了每行特色的从loader中进行映射git
source-map: 原始代码 最好的sourcemap质量有完整的结果,可是会很慢github
webpack中devtool的配置的官方文档在这 :webpack-devtoolweb
反正我看完这些说明是云里雾里, 就我本身而言, 有3个疑问:
eval和sourcemap有什么关系,eval模式是sourcemap吗?
包含cheap关键字的配置中只有行内是什么意思?
这几种不一样的配置有什么区别?
看似配置项不少, 其实只是五个关键字eval
,source-map
,cheap
,module
,inline
的任意组合。这五个关键字每一项都表明一个特性, 这四种特性能够任意组合。它们分别表明如下五种特性(单独看特性说明有点不明因此,别急,往下看):
eval: 使用eval包裹模块代码
source-map: 产生.map
文件
cheap: 不包含列信息(关于列信息的解释下面会有详细介绍)也不包含loader的sourcemap
module: 包含loader的sourcemap(好比jsx to js ,babel的sourcemap)
inline: 将.map
做为DataURI嵌入,不单独生成.map
文件(这个配置项比较少见)
了解了以上各类不一样特性, 再来逐一解答以上问题。
eval
和source-map
都是webpack中devtool的配置选项, eval
模式是使用eval
将webpack中每一个模块包裹,而后在模块末尾添加模块来源//# souceURL
, 依靠souceURL
找到原始代码的位置。包含eval关键字的配置项并不单独产生.map
文件(eval模式有点特殊, 它和其余模式不同的地方是它依靠sourceURL来定位原始代码, 而其余全部选项都使用.map
文件的方式来定位)。包含source-map
关键字的配置项都会产生一个.map
文件,该文件保存有原始代码与运行代码的映射关系, 浏览器能够经过它找到原始代码的位置。(注:包含inline
关键字的配置项也会产生.map
文件,可是这个map文件是通过base64编码做为DataURI嵌入),举个栗子:eval-source-map
是eval
和source-map
的组合,可知使用eavl
语句包括模块,也产生了.map
文件。webpack将.map
文件做为DataURI替换eval
模式中末尾的//# souceURL
。按照我本身的理解, eval
和.map
文件都是sourcemap实现的不一样方式,虽然大部分sourcemap的实现是经过产生.map
文件, 但并不表示只能经过.map
文件实现。下面是eval模式后产生的模块代码:
这里的列信息指的是代码的不包含原始代码的列信息。 官方文档对于包含cheap
的解释是这样的:
> cheap-source-map - A SourceMap without **column-mappings**. SourceMaps > from loaders are not used.
这句话翻译过来就是“在cheap-source-map模式下sourcemap不包含列信息,也不包含loaders的sourcemap”这里的“column-mappings”就是代码列数的意思,是否包含loaders的sourcemap有什么区别将在以后提到。debug的时候大部分人都只在乎代码的行数, 不多关注列数, 列数就是该行代码从第一个字符开始到定位字符的位置(包括空白字符)包含cheap
关键字的模式不包含列信息,体如今webpack中就是:若是包含cheap
关键字,则产生的.map
文件不包含列信息。也就是说当你在浏览器中点击该代码的位置时, 光标只定位到行数,不定位到具体字符位置。而不包含cheap
关键字时, 点击控制台log将会定位到字符位置。
包含列信息后点击原始代码的定位,注意光标位置:
不包含列信息的光标位置:
这篇博客:Go to a line number at a specific column直观地展现了列数的概念。若是深刻到webpack中的细节中体会该配置项,能够看这篇博客:SurviveJS:Source Maps ,该文章对比了webpack中全部配置项中.map
文件的代码,这里截取eval-source-map
和cheap-source-map
的模式产生的.map
文件代码中的mappings
字段对比:
devtool: 'eval-source-map'
"mappings": "AAAAA,QAAQC,GAAR,CAAY,aAAZ",
devtool: 'cheap-source-map'
"mappings": "AAAA",
注:这里使用了VLQ编码,(关于VLQ编码还可参考这里:前端构建:Source Maps详解) 在VLQ编码中,逗号,
表示字符列分割,分号;
表示行分割。包含cheap
关键字的配置项不包含列信息,也就没有逗号。关于VLQ编码, 本文最初的阮一峰的文章中有所解释。而不包含loader的sourcemap指的是不包含loader的sourcemap,不包含它时候若是你使用了诸如babel等代码编译工具时, 定位到的原始代码将是通过编译后的代码位置,而非原始代码。
好比当我用babel编译JS的时候,若是包含不包含loaders的sourcemap,此时debug到的将是编译后的代码, 而非原始代码,如图(这是使用cheap-source-map模式未包含loaders的sourcemap状况下的截图, debug的位置与以前的对比截图是同一个地方):
经过以上两个问题的解释, webpack中的sourcemap各个配置项异同应该有了必定认识,乍看之下各个配置项很难记忆, 但其实从每一个关键字所表明的特性入手, 就能体会到他们的异同。他们在webpack中的主要区别一个体如今重构的性能上, 总的来讲eval
性能最好,source-map
性能最低,但就我自身的实践来看大多用的是最完整的source-map
,该模式对于不论是js仍是css,scss等都能很好的覆盖, 相反其余模式都不完整, 在开发环境下重构性能彷佛比不上功能的完善。
另外须要补充的是module
关键字, 当加上module
关键字webpack将会添加loader的sourcemap。
这篇博客:Webpack devtool source map 对于各个sourcemap配置项都做了对比和梳理, 有趣的是,做者在该文中也指出对于不少官方文档的不解,好比对于所谓的without column-mappings
做者就不知道在讲什么:
A SourceMap without column-mappings. SourceMaps from loaders are not
used.No idea what that means