6. source-map

128 阅读6分钟

source-map作用

我们的代码通常运行在浏览器上时,是通过打包压缩的,也就是真实跑在浏览器上的代码,和我们编写的代码其实是有差异的。 比如:

  1. ES6的代码可能被转换成ES5
  2. 对应的代码行号、列号在经过编译后肯定会不一致
  3. 代码进行丑化压缩时,会将编码名称等修改
  4. 使用了TypeScript等方式编写的代码,最终转换成JavaScript

当代码报错需要调试时(debug),调试转换后的代码是很困难的,source-map 可以调试这种转换后不一致的代码,source-map是从已转换的代码,映射到原始的源文件,使浏览器可以重构原始源并在调试器中显示重建的原始源。

source-map过程

QQ截图20231031214556.png

source-map使用

  1. 根据源文件,生成source-map文件,webpack在打包时,可以通过配置生成 source-map
  2. 在转换后的代码,最后添加一个注释,它指向sourcemap//# sourceMappingURL=common.bundle.js.map,浏览器会根据我们的注释,查找相应的source-map,并且根据 source-map 还原我们的代码,方便进行调试。 webpack 有了 source-map 的配置之后,打包会自动生成 source-map 文件。
module.exports = {
    devtool: "source-map"
}

打包之后
QQ截图20231031215359.png
执行打包后的文件最后一行会有注释指向该打包文件的 source-map 文件。浏览器根据这个注释,自动下载 source-map 文件。
QQ截图20231031215510.png
打包后的文件和 source-map 文件都有了之后,浏览器会还原源代码,可以排查代码错误。
QQ截图20231031215959.png 如果打包后的代码有报错,可以直接在报错信息看到原代码文件。 QQ截图20240404181548.png

chrome 支持 source-map

打开控制台,点击齿轮图标。 QQ截图20231031220310.png

source-map 文件内容解读

{
    // version:当前使用的初source-map版本,也就是最新的第三版
    "version": 3,
    // sources:从哪些文件转换过来的source-map和打包的代码(最初始的文件)    
    "sources": [
        "webpack:./src/index.js",
        "webpack:./src/js/format.js",
        "webpack:./src/js/math.js",
        "webpack:/webpack/bootstrap",
        "webpack:k/webpack/runtime/compat get default export",
        "webpack:/webpack/runtime/define property getters",
        "webpack:/webpack/runtime/hasOwnProperty shorthand",
        "webpack:/webpack/runtime/make namespace object",
        "webpack:/webpack/startup"
    ],
    // names:转换前的变量和属性名称
    "names": [
        "require",
        "sum",
        "mul",
        "console",
        "log",
        "dateFormat",
        "priceFormat",
        "abc",
        "date",
        "price",
        "module",
        "exports",
        "num1",
        "num2"
    ],
    // source-map用来和源文件映射的信息(比如位置信息等)base64编码
    "mappings": ";;;;;;;;;;;;;AAAA;eACqBA,mBAAO,CAAC,mCAAD,C;IAApBC,G,YAAAA,G;IAAKC,G,YAAAA,G,EAEb;;;AACA;AAEAC,OAAO,CAACC,GAAR,CAAYH,GAAG,CAAC,EAAD,EAAK,EAAL,CAAf;AACAE,OAAO,CAACC,GAAR,CAAYF,GAAG,CAAC,EAAD,EAAK,EAAL,CAAf;AAEAC,OAAO,CAACC,GAAR,CAAYC,sDAAU,CAAC,KAAD,CAAtB;AACAF,OAAO,CAACC,GAAR,CAAYE,uDAAW,CAAC,KAAD,CAAvB;AAEAH,OAAO,CAACC,GAAR,CAAYG,GAAZ,E;;;;;;;;;;ACZA,IAAMF,UAAU,GAAG,SAAbA,UAAa,CAACG,IAAD,EAAU;AAC3B,SAAO,YAAP;AACD,CAFD;;AAIA,IAAMF,WAAW,GAAG,SAAdA,WAAc,CAACG,KAAD,EAAW;AAC7B,SAAO,QAAP;AACD,CAFD,C,CAIA;;;AAEAC,MAAM,CAACC,OAAP,GAAiB;AACfN,YAAU,EAAVA,UADe;AAEfC,aAAW,EAAXA;AAFe,CAAjB,C;;;;;;;;;;;;;;;;ACVO,IAAML,GAAG,GAAG,SAANA,GAAM,CAACW,IAAD,EAAOC,IAAP,EAAgB;AACjC,SAAOD,IAAI,GAAGC,IAAd;AACD,CAFM;AAIA,IAAMX,GAAG,GAAG,SAANA,GAAM,CAACU,IAAD,EAAOC,IAAP,EAAgB;AACjC,SAAOD,IAAI,GAAGC,IAAd;AACD,CAFM,C;;;;;;UCJP;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;;WCrBA;WACA;WACA;WACA,cAAc,0BAA0B,EAAE;WAC1C,cAAc,eAAe;WAC7B,gCAAgC,YAAY;WAC5C;WACA,E;;;;;WCPA;WACA;WACA;WACA;WACA,wCAAwC,yCAAyC;WACjF;WACA;WACA,E;;;;;WCPA,6CAA6C,wDAAwD,E;;;;;WCArG;WACA;WACA;WACA,sDAAsD,kBAAkB;WACxE;WACA,+CAA+C,cAAc;WAC7D,E;;;;UCNA;UACA;UACA;UACA",
    // 打包后的文件(浏览器加载的文件)
    "file": "js/bundle.js",
    // 所有的sources相对的根目录
    "sourceRoot": ""
}

source-map 可配置项

webpack 提供了非常多的选项(目前是26个),来处理 source-map,选择不同的值,生成的 source-map 会稍微有差异,打包的过程也会有性能的差异,可以根据不同的情况进行选择。 Devtool | webpack 中文文档 (docschina.org)

1. 下面几个值不会生成 source-map 文件

  • false:不使用source-map,也就是没有任何和source-map相关的内容。
  • none:production 模式下的默认值,不生成source-map。
    以上两种方式没办法还原源代码。
  • eval:development 模式下的默认值,不生成source-map,但是它会在打包后的代码中 eval 执行的代码中,添加 //# sourceURL=; 被浏览器在执行时解析,并且在调试面板中生成对应的一些文件目录,方便我们调试代码。eval函数将原来的代码加上字符串,使用 eval 的目的是 eval 函数后面可以加//# sourceURL=;eval 虽然没有生成 map 文件,但是可以还原源代码,打包速度快。 image.png

2. 生成 source-map 文件的配置

  • source-map
    生成了独立的 source-map 文件,在 bundle 文件中有注释,指向 source-map 文件。 QQ截图20231031231101.png
  • eval-source-map
    会生成 sourcemap,但是 source-map 是以 base64 的方式添加到 eval 函数的后面。source-map文件和 build.js 文件放在一起了。 image.png
  • inline-source-map
    作用:生成 sourcemap,但是 source-map 是以 base64 添加到 bundle 文件的后面。 image.png
  • cheap-source-map
    作用:生成 sourcemap,但是会更加高效一些(cheap低开销),因为它没有生成列映射(Column Mapping) image.png
  • cheap-module-source-map
    生成 sourcemap,类似于cheap-source-map,但是对源自loader的sourcemap处理会更好(例如 ts-loader将 ts 文件转换为 js 文件,babel会把箭头函数等转换为js)
    cheap-source-map和cheap-module-source-map的区别?
    源代码:
const dataFormat = () => {
    return "2020-12-12"
}
const priceFormat = () => {
    return "100.00"
}
module.export = {
    dataFormat,
    priceFormat
}

QQ截图20231031232959.png babel 配置后
QQ截图20231031233828.png
打包后的代码:
image.png
上面的截图就能看出 "cheap-source-map"对loader处理的代码上面加了多余的代码。"cheap-module-source-map"处理后的代码和源代码完全相同。

  • hidden-source-map 生成 sourcemap,但是不会对 source-map 文件进行引用,相当于删除了打包文件中对sourcemap的引用注释
// 被删除掉的 
//# sourceMappingURL=bundle.js.map

手动添加进来,那么 sourcemap 就会生效了。

  • nosources-source-map 生成sourcemap,但是生成的sourcemap只有错误信息的提示,不会生成源代码文件;点击错误提示,无法查看源码: image.png

sourceMap 多个值的组合

webpack 提供给我们的26个值,是可以进行多组合的。
组合的规则如下:

  • inline-|hidden-|eval:三个值时三选一
  • nosources:可选值
  • cheap可选值,并且可以跟随module的值
    [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

source-map 最佳实践

  • 开发阶段:推荐使用 source-map 或者 cheap-module-source-map ,这分别是vue和react使用的值,可以获取调试信息,方便快速开发。
  • 测试阶段:推荐使用 source-map或者cheap-module-source-map 测试阶段我们也希望在浏览器下看到正确的错误提示。
  • 发布阶段:false、缺省值(不写)