Webpack的source map

一、webpack中source map是什么

目前我们的例子都是在浏览器里运行编译打包后的代码,编译后的代码会把我们的原始代码做压缩整合等操作。这样的代码与原始代码差别非常大,对于我们开发时是没有帮助的,我们很难调试编译后的代码。

举一个例子, 配套例子是github仓库 https://github.com/jruit/webpack-tutorial 的webpack5-3

这个例子里需要打包的只有一个JS文件,代码很简单,定义两个变量name和age,分别在控制台打印。在打印name前在代码里打了一个断点debugger。

  // a.js
  let name = 'Jack';
  debugger;
  console.log(name);
  let age = 18;
  console.log(age);

我们npm install安装好相应的npm包后,执行npx webpack-dev-server,在chrome打开http://localhost:8080/

打开chrome调试工具观察浏览器页面,我们发现代码在bundle.js的9681行处被断点暂停。

Webpack source map Webpack教程 姜瑞涛的官方网站

现在在浏览器运行的JS代码已经不是我们原始的a.js了,而是变成了编译后的bundle.js,而且编译后的文件是九千多行代码,并不利于我们开发调试。

因为我们现在只有一个简单的a.js文件,所以还能从打包后的bundle.js找到和a.js的代码关系来做调试工作,如果JS文件多了,工程复杂了,想从打包后的文件找到对应关系来修改代码是一件成本非常高的事情。

如果想要在浏览器里直接看到打包前的代码,这个时候就需要使用source map了。

配套例子是github仓库 https://github.com/jruit/webpack-tutorial 的webpack5-4

要开启source map功能很简单,只需要在Webpack的配置文件里加一行配置就可以了

  // webpack.config.js
  // ...
  devtool: 'source-map',
  // ...

我们在webpack.config.js里增加了一个名字是devtool的配置项,并取值是字符串'source-map'。

现在我们退出之前的命令行程序,重新执行npx webpack-dev-server,然后刷新浏览器,观察页面。

我们发现代码在断点处暂停了,但这次与上次不同,这次是在原始的a.js里的断点出暂停的。

Webpack source map Webpack教程 姜瑞涛的官方网站

因为现在有了对应的编译前原始代码对照,我们可以很轻松在浏览器里进行调试工作了,这就是source map的作用。

二、webpack中source map配置项devtool的取值理解

source map最初是一个单独的文件,浏览器可以通过它还原出编译前的原始代码。

devtool取值为'source-map'是会生成单独的source map文件的,取一些其它值会把source map直接写到编译打包后的文件里,不过浏览器依然可以通过它还原出编译前的原始代码。

因为我们现在是通过webpack-dev-server开启的服务,生成的source map文件直接放在内存里,所以在项目工程目录是看不到这个文件的。

现在我们直接使用npx webpack命令打包,这个时候就会看到磁盘工程目录新生成了bundle.js.map这个文件,这个就是source map文件。

Webpack配置文件的devtool项是用来配置生成何种形式的source map的。除了使用'source-map',还有很多其它选择。

Webpack source map Webpack教程 姜瑞涛的官方网站

我们可以看一下Webpack官网列出的devtool取值,我们对其进行部分截图

可以看到,devtool的取值有20多种,下面我们对其做一下说明。

打包速度的快慢分了六档来表示,从慢到快依次用slowest,slower,slow,fast,faster和fastest表示。build列表示的是初次打包的速度,rebuild表示修改代码后保存再次打包的速度。

production列表示是否可用于生产环境,yes表示可以用于生产环境,no一般用于开发环境。

quality列表示source map映射的原始代码质量,表格里的表示不容易理解,我们可以通过接下来的内容理解。

devtool有部分取值没有给出build和quality等列信息,我们现阶段就不用使用了,只使用那些有列信息的。

这么多取值种类,我们在写Webpack配置文件的时候,该取哪个呢?

我们仔细观察这些取值,会发现基本都是cheap,module,inline,eval和hidden这五个词的组合,最后再附加上source-map(除了单独的eval和source-map这两个取值)。

理解了这五个词的含义,这就会知道该取哪个值作为devtool项。

  • cheap:速度较快的一种选择,因为生成的source map中不会有列信息而只有行信息,编译计算量少,不过loader输出的source map信息不会被采用
  • module: loader输出的source map信息会被采用,这样可以看到loader处理前的原始代码
  • inline: 生成的source map是经过Base64格式编码的,这个词也会产生map文件,但map文件是经过Base64编码后,作为DataURI内嵌
  • eval: 使用eval包裹模块代码,可以提高rebuild的速度
  • hidden:bundle里不包含source map的引用地址,这样浏览器开发者工具里看不到原始代码

三、webpack中开发环境与生产环境该如何对source map配置项devtool取值

开发环境

在开发环境,我们可以对devtool取值为eval-cheap-module-source-map,该配置值能保留loader处理前的原始代码信息,而打包速度也不错,是一个较佳的选择。

生产环境

在生产环境,我们通常是不需要source map的,因为有泄露原始代码的风险,除非你想要定位线上的错误。

生产环境的代码,我们都会使用插件对其进行压缩,因此可以也需要考虑到压缩插件支持完整source-map的能力。因此,生产环境一般只能从source-map,hidden-source-map和nosources-source-map这三个值中选择一个。

取source-map的话,是比较利于定位线上问题和调试代码的,但其它人都可以通过浏览器开发者工具看到原始代码,这是有严重安全隐患的,因此不推荐生产环境用这个值。

nosources-source-map安全性稍微高一些,我们仍可以通过浏览器开发者工具看到原始代码的目录结构,但不会看到具体代码内容。对于错误信息,我们可以在开发者工具的控制台看到原始代码的堆栈信息,对于调试和定位错误基本够用了。不过这种方式不是最安全的,因为仍然可以通过反编译来获取源代码。

hidden-source-map是最安全的取值,这种方式会打包出完整的source map文件,但打包出的bundle里不会有source map文件的地址,因此在浏览器开发者工具里是看不到原始代码的。要想看到原始代码,我们通常会用一些错误收集系统,将source map文件传到该系统上,然后通过JavaScript出错后上报的错误信息,错误收集系统通过source map分析出原始代码的错误堆栈。

在生产环境,除了这些选择外,我们还可以使用服务器白名单策略。我们仍然打包出完整的source map文件上传,但只有白名单的用户才可以看到source map文件。例如,我们可以通过配置服务器对指定的IP地址授权访问source map文件。

小结

本节主要讲解了Webpack里source map是什么以及如何通过devtool配置其生成方式。在开发环境我们选择eval-cheap-module-source-map;生产环境我们一般不生成source map,如果一定需要的话,可以选择hidden-source-map或白名单策略。

笔记与思考

发表评论

邮箱地址不会被公开。 必填项已用*标注