JavaScript脚本正变得越来越复杂,大部分代码都经过了转换、压缩、混淆、多文件合并等过程后才投入到生产环境使用,即使是在开发环境也会进过转换、文件合并、编译后才能运行。导致代码难已debug。
通常代码报错时,解析器会告诉你代码的第几行第几列出错。但是这种转换后的代码毫无意义,因为代码可能被压缩为一行或几行,所有内部变量都已改名。无法得知出错的代码原始位置。
Source Map就能解决这种问题。
什么是Source Map
Source Map 是保存源代码映射关系的文件,可以把转换后的代码映射为转换前的代码,这样就能很好的调试。
在webpack中使用Source Map
在使用webpack 打包项目时,可以使用devtool 开启Source Map让我们可以调试代码。目前 webpack 给出了13种devtool的配置项,如图:
生产环境使用
| devtool | 解释 | | ---|------|------| |(none) | 不生成 source map| |source-map | 生产独立的.map文件,并在 bundle 末尾追加注释| |hidden-source-map | 和 source-map 一样,但不会在 bundle 末尾追加注释| |nosources-source-map | 创建的 source map 不包含 sourcesContent(源代码内容)|
开发环境使用
devtool | 解释 | ---|------|------ eval | 每个 module 会封装到 eval 里包裹起来执行,并且会在末尾追加注释 //@ sourceURL eval-source-map | 每个 module 会通过 eval() 来执行,并且生成一个 DataUrl 形式的 SourceMap cheap-eval-source-map | 类似 eval-source-map,但没有列信息(column-mappings) cheap-module-eval-source-map | 类似 cheap-eval-source-map 同时 loader 的 sourcemap 也被简化为只包含对应行的
特定环境使用
devtool | 解释 | ---|------|------ inline-source-map | source map 转换为 DataUrl 后添加到 bundle 中 cheap-source-map | 没有列映射(column mapping)的 source map,忽略 loader source map inline-cheap-source-map | 类似 cheap-source-map,但是 source map 转换为 DataUrl 后添加到 bundle 中 cheap-module-source-map | 没有列映射(column mapping)的 source map,将 loader source map 简化为每行一个映射(mapping) inline-cheap-module-source-map | 类似 cheap-module-source-map,但是 source mapp 转换为 DataUrl 添加到 bundle 中
是不是有点晕,晕就对了。
解答
看似配置项很多, 其实只是五个关键字eval,source-map,cheap,module,inline的任意组合。这五个关键字每一项都代表一个特性, 这四种特性可以任意组合。它们分别代表以下五种特性。
- eval: 使用eval包裹模块代码
- source-map: 产生.map文件
- cheap: 不包含列信息射(column mapping)
- module: 包含loader的sourcemap(比如jsx to js ,babel的sourcemap)
- inline: 将.map作为DataURI嵌入,不单独生成.map文件
eval
eval和source-map都是webpack中devtool的配置选项, eval模式是使用eval将webpack中每个模块包裹,然后在模块末尾添加模块来源//# souceURL, 依靠souceURL找到原始代码的位置。包含eval关键字的配置项并不单独产生.map文件。
webpackJsonp([1],[
function(module,exports,__webpack_require__){
eval(
...
//# sourceURL=webpack:///./src/js/index.js?'
)
},
function(module,exports,__webpack_require__){
eval(
...
//# sourceURL=webpack:///./src/static/css/app.less?./~/.npminstall/css-loader/0.23.1/css-loader!./~/.npminstall/postcss-loader/1.1.1/postcss-loader!./~/.npminstall/less-loader/2.2.3/less-loader'
)
}
...])
source-map
包含source-map关键字的配置项都会产生一个.map文件,该文件保存有原始代码与运行代码的映射关系, 浏览器可以通过它找到原始代码的位置。
webpackJsonp([1],[
function(e,t,i){...},
function(e,t,i){...},
function(e,t,i){...},
function(e,t,i){...},
...
])
//# sourceMappingURL=index.js.map
与此同时,你会发现你的 output 目录下多了一个 index.js.map 文件。 index.js.map 格式化后如下:
{
"version":3,
"sources":[
"webpack:///js/index.js",
"webpack:///./src/js/index.js",
"webpack:///./~/.npminstall/css-loader/0.23.1/css-loader/lib/css-base.js",
...
],
"names":["webpackJsonp","module","exports"...],
"mappings":"AAAAA,cAAc,IAER,SAASC...",
"file":"js/index.js",
"sourcesContent":[...],
"sourceRoot":""
}
inline-source-map
包含inline关键字的配置项也会产生.map文件,但是这个map文件是经过base64编码作为DataURI嵌入。
webpackJsonp([1],[
function(e,t,i){...},
function(e,t,i){...},
function(e,t,i){...},
function(e,t,i){...},
...
])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9...
eval-source-map
eval-source-map是eval和source-map的组合,可知使用eavl语句包括模块,也产生了.map文件。webpack将.map文件作为DataURI替换eval模式中末尾的//# souceURL。
webpackJsonp([1],[
function(module,exports,__webpack_require__){
eval(
...
//# sourceMappingURL=data:application/json;charset=utf-8;base64,...
)
},
function(module,exports,__webpack_require__){
eval(
...
//# sourceMappingURL=data:application/json;charset=utf-8;base64,...
)
}
...
]);
cheap
如果包含cheap关键字,则产生的.map文件不包含列信息。也就是说当你在浏览器中点击该代码的位置时, 光标只定位到行数,不定位到具体字符位置。而不包含cheap关键字时, 点击控制台log将会定位到字符位置。