加载 Source Map

由于在开发过程中经常会使用新语言去开发项目,最后会把源码转换成能在浏览器中直接运行的 JavaScript 代码。 这样做虽能提升开发效率,在调试代码的过程中你会发现生成的代码可读性非常差,这给代码调试带来了不便。

Webpack 支持为转换生成的代码输出对应的 Source Map 文件,以方便在浏览器中能通过源码调试。 控制 Source Map 输出的 Webpack 配置项是 devtool,它有很多选项,下面来一一详细介绍。

devtool 含义
不生成 Source Map
eval 每个 module 会封装到 eval 里包裹起来执行,并且会在每个 eval 语句的末尾追加注释 //# sourceURL=webpack:///./main.js
source-map 会额外生成一个单独 Source Map 文件,并且会在 JavaScript 文件末尾追加 //# sourceMappingURL=bundle.js.map
hidden-source-map source-map 类似,但不会在 JavaScript 文件末尾追加 //# sourceMappingURL=bundle.js.map
inline-source-map source-map 类似,但不会额外生成一个单独 Source Map 文件,而是把 Source Map 转换成 base64 编码内嵌到 JavaScript 中
eval-source-map eval 类似,但会把每个模块的 Source Map 转换成 base64 编码内嵌到 eval 语句的末尾,例如 //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW...
cheap-source-map source-map 类似,但生成的 Source Map 文件中没有列信息,因此生成速度更快
cheap-module-source-map cheap-source-map 类似,但会包含 Loader 生成的 Source Map

其实以上表格只是列举了 devtool 可能取值的一部分, 它的取值其实可以由 source-mapevalinlinehiddencheapmodule 这六个关键字随意组合而成。 这六个关键字每个都代表一种特性,它们的含义分别是:

该如何选择

如果你不关心细节和性能,只是想在不出任何差错的情况下调试源码,可以直接设置成 source-map,但这样会造成两个问题:

为了解决以上两个问题,可以这样做:

在生产环境下通常不会把 Source Map 上传到 HTTP 服务器让用户获取,而是上传到 JavaScript 错误收集系统,在错误收集系统上根据 Source Map 和收集到的 JavaScript 运行错误堆栈计算出错误所在源码的位置。

不要在生产环境下使用 inline 模式的 Source Map, 因为这会使 JavaScript 文件变得很大,而且会泄露源码。

加载现有的 Source Map

有些从 Npm 安装的第三方模块是采用 ES6 或者 TypeScript 编写的,它们在发布时会同时带上编译出来的 JavaScript 文件和对应的 Source Map 文件,以方便你在使用它们出问题的时候调试它们;

默认情况下 Webpack 是不会去加载这些附加的 Source Map 文件的,Webpack 只会在转换过程中生成 Source Map。 为了让 Webpack 加载这些附加的 Source Map 文件,需要安装 source-map-loader 。 使用方法如下:

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        // 只加载你关心的目录下的 Source Map,以提升构建速度
        include: [path.resolve(root, 'node_modules/some-components/')],
        use: ['source-map-loader'],
        // 要把 source-map-loader 的执行顺序放到最前面,如果在 source-map-loader 之前有 Loader 转换了该 JavaScript 文件,会导致 Source Map 映射错误
        enforce: 'pre'
      }
    ]
  }
};

由于 source-map-loader 在加载 Source Map 时计算量很大,因此要避免让该 Loader 处理过多的文件,不然会导致构建速度缓慢。 通常会采用 include 去命中只关心的文件。

再安装新引入的依赖:

npm i -D source-map-loader

重启 Webpack 后,你就能在浏览器中调试 node_modules/some-components/ 目录下的源码了。