通俗的来说, Source Map
就是一个信息文件,里面存储了代码打包转换后的位置信息,实质是一个 json
描述文件,维护了打包前后的代码映射关系。关于 Source Map
的解释可以看下 Introduction to JavaScript Source Maps[7]。
我们线上的代码一般都是经过打包的,如果线上代码报错了,想要调试起来,那真是很费劲了,比如下面这个例子:
使用打包工具 Webpack
,编译这一段代码
console.log('source map!!!')
console.log(a); //这一行肯定会报错
浏览器打开后的效果:
点击进入报错文件之后:
这根本没法找到具体位置以及原因,所以这个时候, Source Map
的作用就来了, Webpack
构建代码中,开启 Source Map
:
然后重新执行构建,再次打开浏览器:
可以发现,可以成功定位到具体的报错位置了,这就是 Source Map
的作用。需要注意一点的是, Source Map
并不是 Webpack
特有的,其他打包工具同样支持 Source Map
,打包工具只是将 Source Map
这项技术通过配置化的方式引入进来。关于打包工具,下文会有介绍。
上面的案例只是 Source Map
的初体验,现在来说一下它的作用,我们为什么需要 Source Map
?
阮一峰老师的JavaScript Source Map 详解[8]指出,JavaScript 脚本正变得越来越复杂。大部分源码(尤其是各种函数库和框架)都要经过转换,才能投入生产环境。
常见的源码转换,主要是以下三种情况:
这三种情况,都使得实际运行的代码不同于开发代码,除错( debug
)变得困难重重,所以才需要 Source Map
。结合上面的例子,即使打包过后的代码,也可以找到具体的报错位置,这使得我们 debug
代码变得轻松简单,这就是 Source Map
想要解决的问题。
各种主流前端任务管理工具,打包工具都支持生成 Source Map
。
UglifyJS
是命令行工具,用于压缩 JavaScript
代码
安装 UglifyJS
:
npm install uglify - js - g
复制代码
压缩代码的同时生成 Source Map
:
uglifyjs app.js - o app.min.js--source - map app.min.js.map
复制代码
Source Map
相关选项:
--source - map Source Map的文件的路径和名称
--source - map - root 源文件的路径
--source - map - url //#sourceMappingURL的路径。 默认为--source-map指定的值。
--source - map - include - sources 是否将源代码的内容添加到sourcesContent数组
--source - map - inline 是否将Source Map写到压缩代码的最后一行
-- in -source - map 输入Source Map, 当源文件已经经过变换时使用
复制代码
Grunt
是 JavaScript
项目构建工具
配置 grunt-contrib-uglify
插件以生成 Source Map
:
grunt.initConfig({
uglify: {
options: {
sourceMap: true
}
}
});
复制代码
使用 grunt-usemin
打包源码时, grunt-usemin
会依次调用grunt-contrib-concat[9]与grunt-contrib-uglify[10]对源码进行打包和压缩。因此都需要进行配置:
grunt.initConfig({
concat: {
options: {
sourceMap: true
}
},
uglify: {
options: {
sourceMap: true,
sourceMapIn: function(uglifySource) {
return uglifySource + '.map';
},
}
}
});
复制代码
Gulp
是 JavaScript
项目构建工具
使用gulp-sourcemaps[11]生成 Source Map
:
var gulp = require('gulp');
var plugin1 = require('gulp-plugin1');
var plugin2 = require('gulp-plugin2');
var sourcemaps = require('gulp-sourcemaps');
gulp.task('javascript', function() {
gulp.src('src/**/*.js')
.pipe(sourcemaps.init())
.pipe(plugin1())
.pipe(plugin2())
.pipe(sourcemaps.write('../maps'))
.pipe(gulp.dest('dist'));
});
复制代码
SystemJS
是模块加载器
使用SystemJS Build Tool[12]生成 Source Map
:
builder.bundle('myModule.js', 'outfile.js', {
minify: true,
sourceMaps: true
});
复制代码
sourceMapContents
选项可以指定是否将源码写入Source Map
文件Webpack
是前端打包工具(本文案例都会使用该打包工具)。在其配置文件 webpack.config.js
中设置devtool[13]即可生成 Source Map
文件:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
devtool: "source-map"
};
复制代码
Source Map
,可以根据需要进行配置。下文会详细介绍,这里不再赘述。利用 Closure Compiler[14] 生成
生成 Source Map
之后,一般在浏览器中调试使用,前提是需要开启该功能,以 Chrome
为例:
打开开发者工具,找到 Settins
:
勾选以下两个选项:
再回到上面的案例中,源代码文件变成了 index.js
,点击进入后显示真实的源代码,即说明成功开启并使用了 Source Map
还是上面这个案例,执行打包后,生成 dist
文件夹,打开 dist/bundld.js
:
可以看到尾部有这句注释:
//# sourceMappingURL=bundle.js.map
复制代码
正是因为这句注释,标记了该文件的 Source Map
地址,浏览器才可以正确的找到源代码的位置。sourceMappingURL
指向 Source Map
文件的 URL
。
除了这种方式之外,MDN[15]中指出,可以通过 response header
的 SourceMap: <url>
字段来表明。
> SourceMap: /path/to/file.js.map
> ```
`dist` 文件夹中,除了 `bundle.js` 还有 `bundle.js.map` ,这个文件才是 `Source Map` 文件,也是 `sourceMappingURL` 指向的 `URL`
![](https://files.mdnice.com/user/20608/9124506c-2e1d-410e-b141-97062b36fc3f.png)
* `version`:`Source map`的版本,目前为`v3`。
* `sources`:转换前的文件。该项是一个数组,表示可能存在多个文件合并。
* `names`:转换前的所有变量名和属性名。
* `mappings`:记录位置信息的字符串,下文会介绍。
* `file`:转换后的文件名。
* `sourceRoot`:转换前的文件所在的目录。如果与转换前的文件在同一目录,该项为空。
* `sourcesContent`:转换前文件的原始内容。
##### 5.1 关于Source map的版本
在2009年 `Google` 的一篇文章中,在介绍 `Cloure Compiler` 时, `Google` 也趁便推出了一款调试东西: `Firefox` 插件 `Closure Inspector` ,以便利调试编译后代码。这便是 `Source Map` 的初步代啦!
> You can use the compiler with Closure Inspector , a Firebug extension that makes debugging the obfuscated code almost as easy as debugging the human-readable source.
2010年,在第二代即 `Closure Compiler Source Map 2.0` 中, `Source Map` 招认了共同的 `JSON` 格式及其他标准,已几乎具有现在的雏形。最大的差异在于 `mapping` 算法,也是 `Source Map` 的要害地址。第二代中的 `mapping` 已决定运用 `base 64` 编码,可是算法同现在有收支,所以生成的 `.map` 比较现在要大许多。
2011年,第三代即[**Source Map Revision 3 Proposal**](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#)出炉了,这也是咱们现在运用的 `Source Map` 版别。从文档的命名看来,此刻的 `Source Map` 已脱离 `Clousre Compiler` ,演化成了一款独立东西,也得到了浏览器的支撑。这一版相较于二代最大的改动是 `mapping` 算法的紧缩换代,运用[VLQ](https://en.wikipedia.org/wiki/Variable-length_quantity)编码生成[base64](https://zh.wikipedia.org/zh-cn/Base64)前的 `mapping` ,大大缩小了 `.map` 文件的体积。
`Source Map` 发展史的诙谐之处在于,它作为一款辅佐东西被开发出来。毕竟它辅佐的方针日渐式微,而它却成为了技能主体,被写进了浏览器中。
> Source Map V1最初步生成的Source Map文件大概有转化后文件的10倍大。Source Map V2将之减少了50%,V3又在V2的基础上减少了50%。所以现在133k的文件对应的Source Map文件巨细大概在300k左右。
##### 5.2 关于mappings属性
为了避免干扰,将案例改成如下不报错的情况:
```js
var a = 1;
console.log(a);
`
复制代码
打包编译的后 bundle.js
文件:
/******/
(() => { // webpackBootstrap
var __webpack_exports__ = {};
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
var a = 1;
console.log(a);
/******/
})();
//# sourceMappingURL=bundle.js.map
复制代码
打包编译后的 bundle.js.map
文件:
{
"version": 3,
"sources": [
"webpack://learn-source-map/./src/index.js"
],
"names": [],
"mappings": "AAAA;AACA,c",
"file": "bundle.js",
"sourcesContent": [
"var a = 1;\r\nconsole.log(a);"
],
"sourceRoot": ""
}
复制代码
可以看到 mappings
属性的值是:AAAA; AACA, c
,要想说清楚这个东西,需要先解释一下它的组成结构。这是一个字符串,它分成三层:
在回到源代码,就可以分析出:
mappings
中的AAAA
和AACA,c
。console.log(a);
可以拆分出两个位置,分别是console
和log(a)
,所以存在一个逗号。即AACA,c
中的AACA
和c
。总结,就是转换后的源码分成两行,第一行有一个位置,第二行有两个位置。
至于这个 AAAA
, AAcA
等字母是怎么来的,可以参考阮一峰老师的JavaScript Source Map 详解[17]有作详细的介绍。笔者自己的理解是:
AAAA
和 AAcA
以及 c
都是代表了位置,正常来说,每个位置最多由 5 个字母组成,5 个字母的含义分别是:
这里转换后最多只有 4 个字母,是因为没有 names
属性。
每一个位置都可以用VLQ 编码[18]转换,形成一种映射关系。可以在这个网站[19]自己转换测试,将 AAAA; AACA, c
转换后的结果:
可以得到两组数据:
[0, 0, 0, 0]
[0, 0, 1, 0], [14]
复制代码
数字都是从 0
开始的,拿位置 AAAA
举例,转换后得到 [0, 0, 0, 0]
,所以代表的含义分别是;
index.js
。通过以上解析,我们就能知道源代码中 var a = 1;
在打包后文件中,即 bundle.js
的具体位置了。
上文介绍了 Source Map
的作用,原理等。现在说一下打包工具 WebPack
中对 Source Map
的应用,毕竟我们在开发中,都离不开它。
上文有说道,只需要在 webpack.config.js
文件中配置 devtool
就可以使用 Source Map
,这个 devtool
具体的值有哪些,可以参考webpack devtool[20]
的介绍,官方罗列了 20 几种类型,我们当然不能全部都记住,可以记住几个关键的:
建议以下 7 种可选方案:
Source Map
,可以查看错误代码准确信息和源代码的错误位置Source Map
,都在 eval
中,可以查看错误代码准确信息 和 源代码的错误位置。module
会加入 loader
的 Source Map
。内联和外部的区别:
.map
),内联没有。以下通过具体的案例演示上面的 7 种类型:
首先,将案例改成报错状态,为了体现列的情况,将源代码修改成如下:
console.log('source map!!!')
var a = 1;
console.log(a, b); //这一行肯定会报错
复制代码
devtool: 'source-map'
复制代码
编译后,可以查看错误代码准确信息和源代码的错误位置:
生成了 .map
文件:
devtool: 'inline-source-map'
复制代码
编译后,可以查看错误代码准确信息和源代码的错误位置:
但是没有生成 .map文件
,而是以 base64
的形式插入到 sourceMappingURL
中:
devtool: 'hidden-source-map'
复制代码
编译后,可以查看错误代码准确信息,但是无法查看源代码的位置:
生成了 .map
文件:
devtool: 'eval-source-map'
复制代码
编译后,可以查看错误代码准确信息和源代码的错误位置:
但是没有生成 .map文件
,而是在 eval函数
中,包括 sourceMappingURL
:
devtool: 'nosources-source-map'
复制代码
编译后,可以查看无法查看错误代码的准确位置和源代码的错误位置,只能提示错误原因:
生成了 .map
文件:
devtool: 'cheap-source-map'
复制代码
编译后,可以查看错误代码准确信息和源代码的错误位置,但是忽略了具体的列( 因为是b导致报错
):
生成了 .map
文件:
因为需要 module
,所以案例中增加 loader
:
module: {
rules: [{
test: /\.css$/,
use: [
// style-loader:创建style标签,将js中的样式资源插入进去,添加到head中生效
'style-loader',
// css-loader:将css文件变成commonjs模块加载到js中,里面内容是样式字符串
'css-loader'
]
}]
}
复制代码
在 src
目录下新建 index.css
文件,添加样式代码:
body {
margin: 0;
padding: 0;
height: 100%;
background-color: pink;
}
复制代码
然后在 src/index.js
中引入 index.css
:
//引入index.css
import './index.css';
console.log('source map!!!')
var a = 1;
console.log(a, b); //这一行肯定会报错
复制代码
修改 devtool
:
devtool: 'cheap-module-source-map'
复制代码
打包后,打开浏览器,样式生效,说明 loader
引入成功。可以查看错误代码准确信息和源代码的错误位置,但是忽略了具体的列( 因为是b导致报错
):
生成了 .map
文件,同时,将 loader
的信息也一起打包进来:
(1)开发环境:需要考虑速度快,调试更友好
eval
> inline
> cheap
>... )1 . eval-cheap-souce-map
2 . eval-source-map
1 . souce-map
2 . cheap-module-souce-map
3 . cheap-souce-map
最终得出最好的两种方案 --> eval-source-map(完整度高,内联速度快) / eval-cheap-module-souce-map(错误提示忽略列但是包含其他信息,内联速度快)
(2)生产环境:需要考虑源代码要不要隐藏,调试要不要更友好
1 . nosources-source-map
全部隐藏(打包后的代码与源代码)
2 . hidden-source-map
只隐藏源代码,会提示构建后代码错误信息
最终得出最好的两种方案 --> source-map(最完整) / cheap-module-souce-map(错误提示一整行忽略列)
Source Map
是我们日常开发过程中必不可少的,它可以帮助我们调试,定位错误。尽管它涉及非常多的知识点,例如:VLQ[21]、base64[22]等,但是我们核心关注的是它的工作原理,以及在打包工具中,如 webpack
等对 Source Map
的应用。
Source Map
非常强大,不仅在应用于日常开发,还可以做更多的事情,如 性能异常监控平台
。比如FunDebug[23]这个网站就是通过 Source Map
还原生产环境中的压缩代码,提供完整的堆栈信息,准确定位出错误源码,帮助用户快速修复 Bug
,像这样的案例还有许多。
总之,学习 Source Map
是非常有必要的。
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/pSGyJZb53orMZT7ysvtJnQ
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。