2

首发地址: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文件。

sourcemap.png

开启后,如果运行文件设置了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文件:

map.png

同时在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:[<mediatype>][;base64],<data>

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。不会暴露源代码,但仍会暴露反编译后的文件名和结构)

1585058173(1).jpg


夜暮sky
97 声望5 粉丝