为什么?

我们在项目进行打包后,会将开发中的多个文件代码打包到一个文件中,并且经过压缩,去掉多余的空格,且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 的性能影响:
- 缓存机制:使用 cache-loader 和 Terser 缓存避免重复工作
- 并行处理:利用多进程加速压缩过程
- 选择合适的 SourceMap:cheap-source-map 在质量和性能间取得平衡
- 减少冗余信息:禁用 pathinfo 减小文件体积
这样的配置可以在保持 SourceMap 调试功能的同时,最大程度地减少对构建性能的影响。