- sourcemap会影响网页性能吗?
- 如何选择webpack5的devtool属性?
- 如果生产环境没有map文件,出错了如何定位?
Source map介绍
简单理解source map就是存储源码跟实际运行代码的关系映射文件,它不是JavaScript独有的,也同样适用于样式,比如 SASS/LESS编译为CSS,也同样需要map文件,没有sourcemap,报错难以判断出具体哪个模块
有了soucemap,快速定位报错的模块
⚠️注意:: 记得开启浏览器devtools对javascript/CSS sourcemap的支持,基本都默认开启,没有就手动开启下
Source map原理
详见阮老师的JavaScript Source Map 详解 sourcemap本质就是JSON对象,如下是sourcemap的json对象构成元素
{
“version”: 3, // Source map的版本,目前为3
“file”: "out.js", // 转换后的文件名
“sourceRoot”: "",// 转换前的文件所在的目录。如果与转换前的文件在同一目录,该项为空
“sources”: ["foo.js", "bar.js"], // 转换前的文件。该项是一个数组,表示可能存在多个文件合并
“names”: ["src", "maps", "are", "fun"], //转换前的所有变量名和属性名
“mappings”: "AAgBC,SAAQ,CAAEA" //记录位置信息的字符串
}
这也是为什么浏览器加载不到map文件的时候,报的是JSON的解析错误:
在webpack中源码,mappings,打包后的代码三者是如何映射上的,有个可视化工具,有兴趣可以点来看看 source-map-visualization
webpack5中source map的生成
一般编译文件的工具都会伴随有生成sourcemap的功能,比如SASS,TypeScript,这个tools 里面详细列举了有哪些支持sourcemap。以下着重讲一下webpack5中sourcemap的生成。
webppack4之后,development的模式下以eval的形式自动为你产生source maps,除了可以用devtool配置选项,还可以使用SourceMapDevToolPlugin/EvalSourceMapDevToolPlugin有更多的自定义及详细的配置选项,不过不要将devtool和plugin一起用,永远只选择一项
webpack5将devtool的值可以简化为该正则[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map,如何选择的问题总结来说从以下两个方面来讲:
⚠️注意:: 注意区分webpack的版本,每个版本略有不同,比如wepack4从
cheap-module-eval-source-map改为webpack5的eval-cheap-module-source-map,webpack5对关键字的顺序有了严格的规定,以下以webpack5为例
1.生产环境
内联.map会增加打包的包大小,所以生产环境不会选择用内联inline的方式。所以webpack官网生产环境只推荐四个选项
none:不生成 source mapsource-map:整个 source map 作为一个单独的文件生成。它为 bundle 添加了一个引用注释,以便开发工具知道在哪里可以找到它hidden-source-map:与source-map相同,但不会为 bundle 添加引用注释,应用场景经常是和错误监控系统一起使用,比如将sourcemap上传到sentry监控系统nosources-source-map:不暴露源代码,但是出错的时候会有堆栈跟踪,可以看出具体哪个模块出错了,帮你确定了范围
那问题来了,生产环境下你会使用那个?
create-react-app在生产环境下是source-map的选项
- 你会选择让线上的用户看到源代码吗?太赤裸裸了,万一有个大佬直接给你线上code review(想太多),最主要还是因为安全性
- 那没有了map,我又该如何定位线上bug出错的原因?
- 测试环境如果是内网的话,直接
source-map问题也不大,保守点采用nosources-source-map模式,只暴露源码的目录结构与文件命名 hidden-source-map(打包文件没有引用关系)+ 监控系统(Eg:sentry),将map文件上传到sentry中,由它去关联定位线上错误- 网络拦截: 将map文件挑出放到本地服务器,将不含有map文件的部署到服务器,借助第三方软件(例如fiddler),将浏览器对
map文件的请求拦截到本地服务器 - Chrome开发者工具也有添加本地map文件的调试功能,但要一个个添加🤔
2. 开发环境:品质(Qualities) pk 速度(构建/重构建)
wepack官网对品质的介绍 生产环境下的除了
nosources-source-map选项,都是原始源代码的品质,也就是你用map映射出来的源文件跟你实际开发的代码没有差别。而为什么开发环境下要有那么多配置呢?那就是速度跟品质的PK了,取决你debugger的时候定位错误的颗粒度PK构建和重新构建所需要的时间的权衡,先理解几个关键词
eval: 将每个模块都用eval包裹,由于提高的重新构建速度,所以开发环境下推荐使用module: 含有Loaders产生的sourcemaps信息。比如你项目中用了less-loader去编译less,如果使用没含有的Loaders产生的sourcemaps信息的配置,如eval-cheap-source-map,你在浏览器中对看不到该样式对应的less文件,再比如如果你使用了babel-loader,出错了你只看到babel编译后的代码cheap: 没有列信息及Loaders产生的sourcemaps信息。没有列信息?由于我们开发环境下为了可读性一行里面最多一个语句,所以对代码中的列的理解是模糊的。但是假设有这样一行代码console.log("你写了很多代码");throw Error("错误"),它只会告诉你这一行出错了,而不是定位到throw Error("错误")这个语句出错了。所以生产环境没有关于cheap的选项,因为生产环境为了减小体积下都是压缩成一行的。
了解了如上信息,你就理解为什么webpack官网在开发环境的推荐如下配置了
eval- 快速,每个模块都包裹在eval(),没有Loaders产生的sourcemaps信息eval-source-map- 每个模块都使用eval()执行,初始化 source map 时比较慢,但是会在重新构建时提供比较快的速度,Loaders产生的sourcemaps信息,行数能够正确映射eval-cheap-source-map- 为了更快,跟eval-source-map唯一的不同就是牺牲列信息跟Loaders产生的sourcemaps信息eval-cheap-module-source-map- 跟eval-cheap-source-map的区别是有了Loaders产生的sourcemaps信息
那问题又来了,开发环境下你会使用那个?
- 没有Loaders产生的sourcemaps信息不能忍,因为现在很多代码都是经过Loader进行转换的,所以排除
eval-cheap-source-map,eval - 开发环境下你不会在一行内写多个语句,那使用
eval-cheap-module-source-map何乐而不为呢。但是值得一提的是cheap-module-source-map这个配置(没有列信息,将 loader source map 简化为每行一个映射(mapping)),是目前Create-react-app脚手架采用的开发环境的默认sourcemap配置
官网还有对特定场景下使用的devtool配置做了介绍,webpack devtool 特殊场景
sourcemap会影响网页性能吗?
- 如果你以
inline内联的方式直接打包进文件,会。因为通常map会增大包的大小从而影响文件的加载。所以通常不会在生产环境下选择inline的方式嵌入source map,通常用于开发模式,提高构建/重构建的速度 - 如果你是独立的
map文件,几乎不会。map文件只有浏览器开启开发者工具(F12),且开启source map的时候才会去请求相应的map文件。
⚠️注意:你在浏览器的network找不到相应的map请求的,浏览器把
.map文件过滤掉了