MinimalistYing.io

关于 Source Map 以及 Webpack 中 Devtool 的相关配置
2018-08-22
技术
Webpack

Source Map 是什么

为了提升性能,通常我们会把源码压缩混淆后再发布到服务器上。

当线上出现问题时我们很难根据混淆后的代码去具体定位问题出现在哪。

Source Map 的出现正是为了解决这个难题,通过在压缩后的代码中加入如下注释:

// 注意此处的 sourceMappingURL 支持 http uri 以及 data uri 俩种形式
//# sourceMappingURL=/path/to/script.js.map

并在压缩混淆同时生成相应的 map 文件:

{
    version: 3,
    file: "script.js.map",
    sources: [
        "a.js",
        "b.js",
        "c.js"
    ], // 所有源文件的路径
    sourceRoot: "/",
    names: ["foo", "bar", "func"], // 所有源文件中出现的变量名 函数名
    // 关键所在 具体原理就不管了 有点复杂 大致是通过 Base64-VQL 编码做映射
    mappings: "AABASD,ASDFDSA,OAAOC,OAAO..."
}

这样浏览器就可以反向定位到错误具体出现在源码中的哪个文件的哪一行中

关于 Source Map 的几个问题以及解答

为什么在浏览器的 network 中看不到下载 .map 文件的请求

浏览器默认只会在打开开发者工具时才去请求 .map 文件,所以在 network 中看不到记录。

Stackoverflow

如果将 .map 文件也发布到公开的服务器上,普通用户能否利用 Source Map 看到源文件

很不幸,答案是会的。更不幸的是,通过 .map 文件以及压缩后的代码可以反向生成源代码。

这意味着在公开的生产环境下我们不应该去使用 Source Map,因为这样相当于把整个 App 的源码暴露在外,是一种很危险的行为。

建议在类似预发环境(也就是与正式环境相同但是是私有的环境)下来利用 Source Map 进行 Debug。

Ps: 还可以通过服务器端的配置来禁止普通用户下载 .map 文件,这样就可以安全的把 .map 文件也放到正式环境上

Source Map 对开发环境的意义何在

开发环境我们通常不会采用混淆代码,对于简单的 Javascript 应用可能意义并不大。

但当我们在使用 React 或者 Vue 时,Source Map 可以将 Webpack 打包后的文件直接映射为源代码中的 .jsx/.vue 文件。而不利用 Source Map 我们就只能去 Debug 编译过后的文件。所以即使在开发环境下,Source Map 也是能给开发者带来很大帮助的。

Webpack 的 Devtool

Webpack 中可以通过这项配置来决定具体使用何种 Source Map,默认值为 false 也就是不启用 Source Map。

从方便 Debug 的角度从差到好排列,Webpack 一共提供了如下几种形式的 Source Map

  • bundled code // 不开启 浏览器中只能看到打包过后的代码
  • generated code // 可以看到打包前的每个 Module 但是看到的是 Webpack 处理后的文件
  • transformed code // 可以看到打包前的每个 Module 但是看到的是经过 Loader 编译过后的文件
  • original source // 可以看到源码
  • (lines only) // 只定位到行 不定位到列
  • without source content // 不暴露源文件 但是仍会暴露目录结构 可以在错误信息中看到源码对应的出错行列

关于 Devtool 的配置大致由如下几种关键字组合而来,每个关键字代表着不同特性

  • eval // 编译速度最快
  • inline // 不生成 .map 文件 以DataURL的形式嵌入
  • cheap // 只定位到行 transformed code
  • cheap-module // 只定位到行 original source

就目前来说,个人感觉在开发环境下设为 cheap-module-eval-source-map,可以获得较好的 Debug 体验,以及较快的编译速度。

在预发/正式环境下设为 source-map 可以获得最完整的映射。

Ps: Webpack V4 中,当 mode 设为 'production' 时,默认使用的 uglifyjs-webpack-plugin 会默认设置 sourceMap: true。当我们手动去新配置 uglifyjs-webpack-plugin 时会覆盖掉默认配置,所以要注意像如下配置一样加上 sourceMap: true 否则 SourceMap 会不生效。

// webpack.prod.config.js
const UglifyJsPlugin = require("uglifyjs-webpack-plugin")

module.exports = {
    // ...
    mode: 'production',
    optimization: {
        minimizer: [
            new UglifyJsPlugin({
                // ...
                sourceMap: true // 注意这里!!!!!!
                // ...
            })
        ]
    }
    // ...
}

参考