什么是SourceMap?有什么作用?

为什么?

我们在项目进行打包后,会将开发中的多个文件代码打包到一个文件中,并且经过压缩,去掉多余的空格,且babel编译化后,最终会用于线上环境,那么这样处理后的代码和源代码会有很大的差别。

当有bug的时候,我们只能定位到压缩处理后的代码位置,无法定位到开发环境中的代码,对于开发不好调式。

因此sourceMap出现了,它就是为了解决不好调式代码问题的。

在项目开发完进行打包后,在打包的文件夹里通常除了js,css,图片字体等资源文件外,大家一定还见过xxx.js.map的文件。

常见的转换过程包括但不限于:

  • 压缩混淆(UglifyJS)

  • 编译(TypeScript, CoffeeScript)

  • 转译(Babel)

  • 合并多个文件,减少带宽请求。

经过上述转换过程的代码,往往都会变得面目全非,就像下面这样:

这样虽然对带宽很友好,但是调试起来就不是那么轻松了。我们在代码出错的时候,肯定最希望能定位其在源码中的位置。所以可以利用SourceMap插件。

使用SourceMap前:

在很多打包工具如vite等的配置文件中,经常有sourceMap这个配置项,那么sourceMap到底是啥?

一句话解释就是:sourceMap 存储着,打包前的代码到打包后的代码的位置信息

如果你懂得sourceMap计算位置信息的原理的话,就可以根据sourceMap文件,从打包后的经过压缩过的js文件中,推算出代码的原来位置。

但是呢,我们这种菜鸟有必要知道sourceMap算法么?

我觉得还没到那一步呢,我们首先就先知道他是一个什么东西就行,在打包工具的配置文件中,在生产环境一般设置为false,防止被解析,测试环境随意了。

使用

在前端的工作中主要是用来解决以下三个方面出现的 debug 问题:

a. 代码压缩混淆后 b. 利用 sass 、typeScript 等其他语言编译成 css 或 JS 后 c. 利用 webpack 等打包工具进行多文件合并后

上面三种情况,我们在调试时都是没办法像调试源码般轻松,这就需要 SourceMap 帮助我们在控制台中转换成源码,从而进行 debug

SourceMap 其实就是一个存储源代码与编译代码对应位置映射的信息文件。

实际上就是一个 JSON 键值对,利用 VLQ编码与特定的规则存储位置信息。这里面的逻辑有兴趣的可以看看阮老师的这篇文章《 JavaScript Source Map 详解 》

js 复制代码
{
    version : 3, // Source map的版本
    file: "out.js", // 转换后的文件名
    sourceRoot : "", // 转换前的文件所在的目录。如果与转换前的文件在同一目录,该项为空
    sources: ["foo.js", "bar.js"], // 转换前的文件。该项是一个数组,表示可能存在多个文件合并
    names: ["src", "maps", "are", "fun"], // 转换前的所有变量名和属性名
    mappings: "AAgBC,SAAQ,CAAEA" // 记录位置信息的字符串
}

其实不用懂原理直接使用就好,毕竟 .map 文件也是工具生成的,不用自己编写。

webpack中的使用:在 webpack 中使用 source map,是通过配置中的 devtool 字段来设置source map类型。

webpakc中的source map关键字:

  • eval:使用eval包裹块代码
  • source map:产生.map文件
  • cheap:不包含列信息
  • inline:将.map作为dataURL嵌入到js文件内部
  • module:包含loder的sourcemap

webpack中的source map类型大概有26种类型,其实就是根据上面关键字的功能进行的排列组合,分别生成不同类型的Source Map,实际应用中可以根据需要进行配置。

开发环境一般推荐:

  • eval - 每个模块都使用 eval() 执行,并且都有 //@ sourceURL。此选项会非常快地构建。主要缺点是,由于会映射到转换后的代码,而不是映射到原始代码(没有从 loader 中获取 source map),所以不能正确的显示行数。

  • eval-source-map - 每个模块使用 eval() 执行,并且 source map 转换为 DataUrl 后添加到 eval() 中。初始化 source map 时比较慢,但是会在重新构建时提供比较快的速度,并且生成实际的文件。行数能够正确映射,因为会映射到原始代码中。它会生成用于开发环境的最佳品质的 source map

  • eval-cheap-source-map - 类似 eval-source-map,每个模块使用 eval() 执行。这是 "cheap(低开销)" 的 source map,因为它没有生成列映射(column mapping),只是映射行数。它会忽略源自 loader 的 source map,并且仅显示转译后的代码,就像 eval devtool。

  • eval-cheap-module-source-map - 类似 eval-cheap-source-map,并且,在这种情况下,源自 loader 的 source map 会得到更好的处理结果。然而loader source map 会被简化为每行一个映射(mapping)。

实战

webpack配置文件webpack.config.js中设置devtool即可生成Source Map文件:

js 复制代码
const path = require('path');
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  devtool: "source-map"
};

在Vue CLI中默认是开启SourceMap的,只需要在webpack的配置文件中将其设置为true即可:

js 复制代码
module.exports = {
    //...
    productionSourceMap: true
    //...
}

也可以通过vue.config.js文件中进行配置:

js 复制代码
module.exports = {
    productionSourceMap: true
}

在代码打包后,压缩的JS文件会携带SourceMap文件一同发布到服务器。如果你需要禁用它,只需将productionSourceMap设置为false。

js 复制代码
module.exports = {
    devtool: 'source-map'
}

这会生成一个外部的source-map文件,在每个JS文件的末尾添加一个sourceURL注释。这些sourceURL注释指向source-map文件的位置。

js 复制代码
module.exports = {
    devtool: 'cheap-source-map'
}

这种配置方式比上面的选项更快,会忽略列信息,只有行信息。它会生成一个外部的source-map文件。只是这个文件生成的时候,只包含每个打包后的模块的第一行。

js 复制代码
module.exports = {
    devtool: 'inline-source-map'
}

这种选项生成一个base64-encoded inline sourcemap文件,类似于DataUrl。它不会生成外部的source-map文件。

优化:

开启SourceMap会导致一些性能问题,开发者可以通过一些优化配置来减轻这些性能问题。

js 复制代码
module.exports = {
    module: {
        rules: [
            {
              test: /\.(js|vue)$/,
              use: 'cache-loader',
              enforce: true
            }
        ]
    },
    devtool: 'cheap-source-map',
    output: {
        pathinfo: false
    },
    optimization: {
        minimize: true,
        minimizer: [
            new TerserPlugin({
                test: /\.js(\?.*)?$/i,
                cache: true,
                sourceMap: true,
                parallel: true,
                terserOptions: {
                    safari10: true,
                    compress: {},
                    mangle: true
                }
            })
        ]
    },
}

使用Vue SourceMap可以大大提高开发效率,帮助我们快速定位代码错误。同时,为了避免对性能的影响,需要采取一定的优化措施。

这段配置是一个 Webpack 构建优化配置,主要针对 SourceMap 相关的性能问题进行了多项优化。让我详细解释每个部分的作用:

缓存优化 (cache-loader)

js 复制代码
module: {
  rules: [
    {
      test: /\.(js|vue)$/,
      use: 'cache-loader',
      enforce: true
    }
  ]
}
  • 作用:对 JS 和 Vue 文件使用 cache-loader 进行缓存
  • 效果:避免重复编译相同的文件,显著提升 rebuild 速度
  • enforce: true 确保这个 loader 在其他 loader 之前执行

SourceMap 类型选择

js 复制代码
devtool: 'cheap-source-map'
  • 优化点:使用性能更好的 SourceMap 类型
  • cheap-source-map 的特点:
    • 不包含列信息,只包含行信息
    • 不包含 loader 的 sourcemap(如 babel-loader)
    • 生成速度更快,文件体积更小

输出路径信息优化

js 复制代码
output: {
  pathinfo: false
}
  • pathinfo: false:禁用输出路径信息
  • 作用:减少生成的 bundle 中的注释信息,减小文件体积
  • 在生产环境中通常建议设置为 false

代码压缩优化

js 复制代码
optimization: {
  minimize: true,
  minimizer: [
    new TerserPlugin({
      test: /\.js(\?.*)?$/i,
      cache: true,           // 启用缓存
      sourceMap: true,       // 生成 SourceMap
      parallel: true,        // 启用多进程并行压缩
      terserOptions: {
        safari10: true,      // 兼容 Safari 10
        compress: {},        // 压缩配置
        mangle: true         // 代码混淆
      }
    })
  ]
}

TerserPlugin 优化细节:

  • cache: true:启用文件系统缓存,避免重复压缩
  • parallel: true:使用多进程并行运行,提高压缩速度
  • safari10: true:解决 Safari 10 中的循环作用域问题

性能优化总结

这些配置主要通过以下方式减轻 SourceMap 的性能影响:

  1. 缓存机制:使用 cache-loader 和 Terser 缓存避免重复工作
  2. 并行处理:利用多进程加速压缩过程
  3. 选择合适的 SourceMap:cheap-source-map 在质量和性能间取得平衡
  4. 减少冗余信息:禁用 pathinfo 减小文件体积

这样的配置可以在保持 SourceMap 调试功能的同时,最大程度地减少对构建性能的影响。

相关推荐
烛阴1 小时前
带你用TS彻底搞懂ECS架构模式
前端·javascript·typescript
卓码软件测评2 小时前
【第三方网站运行环境测试:服务器配置(如Nginx/Apache)的WEB安全测试重点】
运维·服务器·前端·网络协议·nginx·web安全·apache
龙在天2 小时前
前端不求人系列 之 一条命令自动部署项目
前端
开开心心就好2 小时前
PDF转长图工具,一键多页转图片
java·服务器·前端·数据库·人工智能·pdf·推荐算法
国家不保护废物2 小时前
10万条数据插入页面:从性能优化到虚拟列表的终极方案
前端·面试·性能优化
文心快码BaiduComate2 小时前
七夕,画个动态星空送给Ta
前端·后端·程序员
web前端1232 小时前
# 多行文本溢出实现方法
前端·javascript
文心快码BaiduComate2 小时前
早期人类奴役AI实录:用Comate Zulu 10min做一款Chrome插件
前端·后端·程序员
人间观察员2 小时前
如何在 Vue 项目的 template 中使用 JSX
前端·javascript·vue.js
布列瑟农的星空2 小时前
大话设计模式——多应用实例下的IOC隔离
前端·后端·架构