用过webpack的都知道webpack的sourcemap配置比较麻烦,比如这两个配置的区别:eval-nosources-cheap-module-source-maphidden-module-source-地图不清楚?事实上,这是有规律的。当你配置错误时,webpack会提示你一个规则:^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$就是这个配置规则是几个基本配置的组合。如果你了解了eval、nosources、cheap、module等每一个基本配置,并按照规则进行组合,你也就了解了整体配置。那么这些配置中的每一个是什么意思呢?让我们逐一看看。在说基本配置之前,先说一下sourcemap是什么:sourcemapsourcemap将编译后的代码与源代码关联起来,通过行列号的映射。例如,编译代码的第3行和第4列对应于源代码中的第8行和第5列。这称为映射。sourcemap的格式如下:{version:3,file:"out.js",sourceRoot:"",sources:["foo.js","bar.js"],names:["a",“b”],映射:“AAgBC、SAAQ、CAAEA;AAAEA”,sourcesContent:['consta=1;console.log(a)','constb=2;console.log(b)']}version为版本号,file为文件名,sourceRoot为源码根目录,names为转换前的变量名,sources为源码文件,sourcesContent为内容每个source对应的sourcecode,mappings是位置映射。为什么可以有多个来源?因为编译后的产品可能是多个源文件的组合,比如打包,一个bundle.js对应n个sources源文件。为什么要把变量名单独抽出来放在names里呢?因为这样索引可以通过下标进行索引,映射中不需要保存变量名,只会保留names的索引。关键是mappings部分:mappings部分用分号隔开;和逗号:mappings:"AAAAA,BBBBB;CCCCC"分号代表一条线,这样就不用线映射了。然后每一行可能有多个位置的映射,由.那么每个具体的映射是什么?比如AAAAA一共有五位,每一位的含义不同:第一位:转换后代码所在的列(行数由分号决定;)第二位:转换前对应哪个源代码文件,并保存在sources中,下标用于索引第三位:转换前源代码对应的行。第四位:转换前源代码对应的列。第5位:转换前源码对应的变量名,存储在names中,下标索引编码后变成AAAAA。这种编码方式称为VLQ编码。sourcemap的格式还是很好理解的,就是将编译后代码的位置和源码的位置一一对应起来。各种调试工具一般都支持sourcemap解析,只需要在文件末尾添加这一行://@sourceMappingURL=/path/to/source.js.map运行时会和源码关联:看几个sourcemap配置网页包。前面提到,webpack的sourcemap配置是eval、cheap、nosources、inline、source-map等基本配置的组合。我们先来看看这些基本的配置:evaleval的API是动态执行JS代码的。例如:但是有一个问题,eval的代码无法下断点。如何解决这个问题呢?浏览器支持这样的功能,只要在eval代码末尾加上//#sourceURL=xxx,就会把这段代码添加到xxx名称下的sources中。我们不能就此分手吗?比如:执行完后,会发现sources里多了.js文件:可以打断,比如在add里打断点,然后执行eval。你会发现它会卡住!除了指定源文件外,还可以进一步指定sourcemap映射到源码:这样动态eval代码也可以关联到源码,断点可以打断!webpack使用eval的特性来优化sourcemap生成的性能。比如你可以指定devtool为eval:生成的代码是每个模块都被eval包裹起来,还有一个sourceUrl来指定文件名:这样有什么好处呢?要快,因为你只需要指定一个文件名,而不需要生成一个sourcemap。sourcemap的生成还是很慢的,需要一个一个的处理,比如encoding。各个模块的代码被eval包裹起来,那么执行的时候会在sources中生成相应的文件,这样断点就可以打断了:但是这样只是分割了各个模块的代码,并没有让源码关联起来,如果关联了源码,可以再次打开sourcemap:会发现生成的代码里面也包裹着eval,但是除了sourceUrl之外,还有sourceMappingUrl:再次运行时,除了eval的代码还会生成sources中的文件,还有可以做sourcemap映射:webpack的sourcemap的配置利用了浏览器对eval代码的调试支持。那么为什么这个配置项叫devtool而不是sourcemap呢?因为不仅有sourcemap,还有eval。再看下一个基本配置:source-mapsource-map的配置是生成一个独立的sourcemap文件:可以关联也可以不关联。比如添加hidden会生成sourcemap但不会关联:生产环境不需要关联sourcemap,但是可能需要生成一个sourcemap文件上传到错误管理平台之类的来映射错误位置网上代码到对应的源码。另外,还可以配置成内联:这个是通过dataUrl内联到打包文件中的:这些配置还是比较容易理解的,看下一个基本配置:cheapsourcemap慢主要是处理映射比较慢,在很多不需要映射到源码的行和列的情况,只要精确到行,那么cheap就可以用了。不精确可以提高sourcemap生成的速度,但是会牺牲一些精度:我们看下一个基本配置:一个module在modulewebpack中会被多次处理,比如loaderA转换一次,laoderB转换一次转换,然后打包在一起。每次转换都会生成一个sourcemap,也就是有多个sourcemap:默认的sourcemap只是从bundle到module可以关联的代码,也就是只关联最后一个sourcemap。那么如果要调试原来的源码怎么办呢?然后关联各个loader的sourcemap,就是module配置的作用。比如我们要调试React的原始源码,首先要用sourcemap生成代码:如何用sourcemap生成React代码可以参考我之前的文章。有了sourcemap之后,还需要配置sourcemap-loader:它的作用是读取源代码的sourcemap,传递给后续的loader。然后配置devtool,添加module:再次运行,会发现react代码可以映射到原来的源码:之前只能从bundle.js映射到编译后的module代码,也就是这一步:devtool配置加上module,支持loader的sourcemap映射,然后添加sourmap-loader读取源码的sourcemap,这样就可以一次性映射回原来的源码:当你想调试原来的时候源码,模块的配置起来很有用。接下来是最后一个基本配置:nosourcessourcemap有一个sourceContent部分,也就是直接把源码贴在这里。这样做的好处是即使根据文件路径找不到文件也可以映射,但是这样会增加sourcemap的体积。如果确定可以根据文件路径找到源文件,不生成sourceContent也可以。比如devtool配置为source-map,生成的sourcemap是这样的:当你加上nosources后,生成的sourcemap将没有sourceContent部分:sourcemap的文件大小会小很多。基本配置完成后,接下来就是各种组合了。这个比较简单。即使组合错了,webpack也会提示你按什么顺序组合。就是按照这个规律来检查的:^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$来讨论一下最佳实践:online当然,你需要开启hidden,不关联sourcemap,但是要生成sourcemap,不需要模块映射回原来的源码,所以可能是hidden-source-map。您可以在开发期间使用eval方法。这样每个模块都是独立映射的。不需要从bundle.js开始映射,然后也可以开启cheap。它只映射到源代码的某一行,以提高生成速度。通常,您需要将其映射回模块。原始源代码,所以它可能是eval-cheap-module-source-map。当然,具体的配置是根据需求来的。我们了解每个基本配置并知道如何组合它。不知道有没有同学会觉得这么写很麻烦。每个基础配置能真假配置吗?确实有这样一个插件:SourceMapDevToolPlugin,它有很多选项,比如module、columns、noSources等:相当于devtool的另一种配置方式。要启用它,需要将devtool设置为false。而且它可以控制更多的东西,比如修改sourcemap的url和文件名等:当你需要对sourcemap的生成方式做更多的控制时,可以使用这个webpack插件。综上所述,webpack的sourcemap配置繁琐,其实是有规律的。它是一些基本配置按一定顺序组合而成。了解了每一个基本配置,知道怎么组合之后,就可以了解各种devtool配置了。eval:浏览器devtool支持将eval的内容通过sourceUrl生成文件,并可以进一步通过sourceMappingUrl映射回源码。webpack利用这个特性简化了sourcemap的处理,可以直接从module而不是bundle级别开始映射。cheap:只映射到源代码的某一行,不精确到列,可以提高sourcemap生成速度source-map:生成sourcemap文件,可以配置inline,会以dataURL的形式内联,你可以配置hidden,只生成sourcemap,no将nosources与生成的文件关联:不生成sourceContent内容,可以减小sourcemap文件的大小module:生成sourcemap时,会关联每个loader步骤生成的sourcemap,配合使用sourcemap-loader映射回原始源代码。了解了这些基本的配置项后,按照^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$的规则进行组合,就可以实现sourcemap配置在各种要求下。当然这个sourcemap的配置还不够详细,比如sourcemap的url怎么生成,文件名是什么。如果要配置这些,可以关闭devtool,开启SourceMapDevToolPlugin进行配置。虽然webapck中的sourcemap配置方式很多,但底层是浏览器支持的文件级sourcemap和eval代码的sourcemapping和sourcemap机制。其余的方法都是基于这两种机制的封装。了解了浏览器devtool的机制,webpack封装的基本配置,知道了组合规则,就可以针对各种需求来处理sourcemap配置了。
