首发地址:https://mp.weixin.qq.com/s/Xn...
话不多说,正文从此开始~
Source Map的作用
目前工程源码经过构建工具的转换,主要有如下几种情况:
1. 代码压缩
2. 文件合并
3. 各种DSL解析成javascript,比如jsx转换为js
因此生成的代码与源码差别较大,使得问题排查变的困难。比如jquery.min.js中大部分变量都被重写,压缩后没有保留换行及空格等,一旦出现问题,很难定位源代码的位置。而sourceMap提供了源代码到最终生成代码间的映射关系,能够帮助开发者方便的定位源代码。
Source Map是什么
sourceMap是一个JSON文件,存储着转换后代码和转化前代码的位置对应,以及转换前代码的信息。当执行出错或debugger时,相关工具(比如google的开发者工具)可以根据sourceMap中的信息直接显示原始代码并定位到出错点。
文件格式如下:
{
"version":3, //sourceMap版本
"sources": ["", ""], //转换前的文件,可能由多个文件合并而成
"names":[], //转换前所有变量名和属性名
"mappings":"AAAA,sBACA;aAAA", //记录位置信息的字符串
"file":"main.css", //转换后的文件名
"sourcesContent":["", ""] //source中文件对应的源代码
}
特别说明下mappings属性,字符串中(;)表示行结束符;(,)表示该行中的某个位置。编码方式(VLQ)自行查阅,不再赘述。
sourceMap如何使用
启用sourceMap,需要在转换后的文件尾部,加上其对应的map文件:
js文件中的插入方式:
//# sourceMappingURL=main.96a1ec79aa01119bd12c.js.map
css文件中的插入方式:
/*# sourceMappingURL=main.css.map*/
注意,例子中的map文件是和生成的文件在同一个目录下,同样这里可以把map文件放在cdn上,配置完整路径即可。
谷歌开发者工具调试
打开devTools的Settings,其中Source的配置中,需要将如下两项启用,否则无法加载运行文件中设置的map文件。
开启后,如果运行文件设置了map文件,则会在Source中看到转换前的代码,如下图所示。
有的同学动手操作后可能会问:为什么我设置了map文件,也在source下看到了源代码,为什么在network下没看到请求呢?嗯嗯嗯...请求是发送了,只是没展示出来,怎么验证呢?谷歌打开chrome://net-internals/#events,可以查看捕获的网络日志中存在map文件的请求。
sourceMap如何生成
前面说了一堆怎么使用,那sourceMap文件到底怎么来的呢?
目前webpack盛行,在其配置文件中就有生成source map的设置:devtool,下面是devtool可选的配置值,不同的值会明显影响到构建(build)和重新构建(rebuild)的速度。
原始源代码->loader转换过的代码-> Webpack生成后的代码 | 打包后的代码,详情参见webpack配置指南。
常见的几个配置,测试结果如下:
source-map
构建后,看到生成了map文件:
同时在main.***.js文件中添加//# sourceMappingURL=关联map文件:
//# sourceMappingURL=main.c81dbe1ae672911d1714.js.map
eval-source-map
每个模块使用 eval() 执行,并且source map转换为DataUrl后添加到 eval() 中。【can cache SourceMaps for modules. It's much faster for rebuilds.】
//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIj...ifQ==\n//# sourceURL=webpack-internal:///./src/routes.js\n");
eval-cheap-module-source-map
仅提供行信息,速度更快
eval-cheap-source-map
没有生成列映射,只是映射行数。忽略源自loader的source map(比如jsx到js的映射),并且仅显示转译后的代码
eval
development模式下默认eval。每个模块都使用 eval() 执行,并且都有 //@ sourceURL,不生成映射文件,其对应的是转换后的代码路径。会映射到转换后的代码,而不是映射到原始代码(没有从 loader 中获取 source map),所以不能正确的显示行数。
//# sourceURL=webpack:///./src/pages/info/index.vue
速度对比:source-map < eval-source-map < eval-cheap-module-source-map< eval-cheap-source-map< eval
以下选项只在一些特定场景使用,比如针对一些第三方工具:
inline-source-map
未生成无map文件,如其名inline,映射信息被base64编码以DataUrl的形式写进了main.**.js文件:
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJz...
[扩展] dataUrl的形式如下:
data:[][;base64],
cheap-source-map
和source-map类似,但没有列映射(column mapping)的 source map,忽略loader source map。
cheap-module-source-map
没有列映射(column mapping)的 source map,将 loader source map 简化为每行一个映射(mapping)。
选项总结
- eval:模块转换为字符串,用eval包裹执行;
- inline:将映射信息内联;
- cheap:不记录列的对应关系;
- module:由源码到loader转换后代码的映射。
devtool的配置项基本由上面的几种组合完成。
最后,有同学问了,开发和测试环境分别用哪个?
由于开发环境追求构建速度和debug能力,因此推荐:
cheap-module-eval-source-map
而生产环境,如果不想公开sourceMap文件,可以不设置devtool;反之,则推荐:
source-map
另外,关注下:hidden-source-map(和source-map类似,但不会在生成文件中添加引用注释)、nosources-source-map(创建的source map不包含sourceContent。不会暴露源代码,但仍会暴露反编译后的文件名和结构)