grunt

更新时间:2022-12-28 13:00:24 阅读: 评论:0


2022年12月28日发(作者:日本语能力考试)

【总结】1263-弄懂SourceMap,前端开发提效100%

⼀、什么是SourceMap

通俗的来说,SourceMap就是⼀个信息⽂件,⾥⾯存储了代码打包转换后的位置信息,实质是⼀个json描述⽂件,维护了打包前后的代

码映射关系。关于SourceMap的解释可以看下IntroductiontoJavaScriptSourceMaps

[7]

我们线上的代码⼀般都是经过打包的,如果线上代码报错了,想要调试起来,那真是很费劲了,⽐如下⾯这个例⼦:

使⽤打包⼯具Webpack,编译这⼀段代码

(sourcemap)

(a);//这⼀⾏肯定会报错

浏览器打开后的效果:

点击进⼊报错⽂件之后:

这根本没法找到具体位置以及原因,所以这个时候,SourceMap的作⽤就来了,Webpack构建代码中,开启SourceMap:

然后重新执⾏构建,再次打开浏览器:

可以发现,可以成功定位到具体的报错位置了,这就是SourceMap的作⽤。需要注意⼀点的是,SourceMap并不是Webpack特有的,

其他打包⼯具同样⽀持SourceMap,打包⼯具只是将SourceMap这项技术通过配置化的⽅式引⼊进来。关于打包⼯具,下⽂会有介绍。

⼆、SourceMap的作⽤

上⾯的案例只是SourceMap的初体验,现在来说⼀下它的作⽤,我们为什么需要SourceMap?

阮⼀峰⽼师的JavaScriptSourceMap详解

[8]

指出,JavaScript脚本正变得越来越复杂。⼤部分源码(尤其是各种函数库和框架)都要

经过转换,才能投⼊⽣产环境。

常见的源码转换,主要是以下三种情况:

压缩,减⼩体积

多个⽂件合并,减少HTTP请求数

其他语⾔编译成JavaScript

这三种情况,都使得实际运⾏的代码不同于开发代码,除错(debug)变得困难重重,所以才需要SourceMap。结合上⾯的例⼦,即使

打包过后的代码,也可以找到具体的报错位置,这使得我们debug代码变得轻松简单,这就是SourceMap想要解决的问题。

三、如何⽣成SourceMap

各种主流前端任务管理⼯具,打包⼯具都⽀持⽣成SourceMap。

3.1UglifyJS

UglifyJS是命令⾏⼯具,⽤于压缩JavaScript代码

安装UglifyJS:

npminstalluglify-js-g

复制代码

压缩代码的同时⽣成SourceMap:

复制代码

SourceMap相关选项:

--source-mapSourceMap的⽂件的路径和名称

--source-map-root源⽂件的路径

--source-map-url//#sourceMappingURL的路径。默认为--source-map指定的值。

--source-map-include-sources是否将源代码的内容添加到sourcesContent数组

--source-map-inline是否将SourceMap写到压缩代码的最后⼀⾏

--in-source-map输⼊SourceMap,当源⽂件已经经过变换时使⽤

复制代码

3.2Grunt

Grunt是JavaScript项⽬构建⼯具

配置grunt-contrib-uglify插件以⽣成SourceMap:

nfig({

uglify:{

options:{

sourceMap:true

}

}

});

复制代码

使⽤grunt-umin打包源码时,grunt-umin会依次调⽤grunt-contrib-concat

[9]

与grunt-contrib-uglify

[10]

对源码进⾏打包和压缩。

因此都需要进⾏配置:

nfig({

concat:{

options:{

sourceMap:true

}

},

uglify:{

options:{

sourceMap:true,

sourceMapIn:function(uglifySource){

returnuglifySource+.map;

},

}

}

});

复制代码

3.3Gulp

Gulp是JavaScript项⽬构建⼯具

使⽤gulp-sourcemaps

[11]

⽣成SourceMap:

vargulp=require(gulp);

varplugin1=require(gulp-plugin1);

varplugin2=require(gulp-plugin2);

varsourcemaps=require(gulp-sourcemaps);

(javascript,function(){

(src/**/*.js)

.pipe(())

.pipe(plugin1())

.pipe(plugin2())

.pipe((../maps))

.pipe((dist));

});

复制代码

3.4SystemJS

SystemJS是模块加载器

使⽤SystemJSBuildTool

[12]

⽣成SourceMap:

(,,{

minify:true,

sourceMaps:true

});

复制代码

sourceMapContents选项可以指定是否将源码写⼊SourceMap⽂件

3.5Webpack

Webpack是前端打包⼯具(本⽂案例都会使⽤该打包⼯具)。在其配置⽂件中设置devtool[13]

即可⽣成SourceMap

⽂件:

constpath=require(path);

s={

entry:./src/,

output:{

filename:,

path:e(__dirname,dist)

},

devtool:"source-map"

};

复制代码

devtool有20多种不同取值,分别⽣成不同类型的SourceMap,可以根据需要进⾏配置。下⽂会详细介绍,这⾥不再赘述。

3.6ClosureCompiler

利⽤ClosureCompiler

[14]

⽣成

四、如何使⽤SourceMap

⽣成SourceMap之后,⼀般在浏览器中调试使⽤,前提是需要开启该功能,以Chrome为例:

打开开发者⼯具,找到Settins:

勾选以下两个选项:

再回到上⾯的案例中,源代码⽂件变成了,点击进⼊后显⽰真实的源代码,即说明成功开启并使⽤了SourceMap

五、SourceMap的⼯作原理

还是上⾯这个案例,执⾏打包后,⽣成dist⽂件夹,打开dist/:

可以看到尾部有这句注释:

//#sourceMappingURL=

复制代码

正是因为这句注释,标记了该⽂件的SourceMap地址,浏览器才可以正确的找到源代码的位置。sourceMappingURL指向SourceMap⽂

件的URL。

除了这种⽅式之外,MDN

[15]

中指出,可以通过responheader的SourceMap:字段来表明。

>SourceMap:/path/to/

>```

`dist`⽂件夹中,除了``还有``,这个⽂件才是`SourceMap`⽂件,也是`sourceMappingURL`指向的`URL`

![](/ur/20608/)

*`version`:`Sourcemap`的版本,⽬前为`v3`。

*`sources`:转换前的⽂件。该项是⼀个数组,表⽰可能存在多个⽂件合并。

*`names`:转换前的所有变量名和属性名。

*`mappings`:记录位置信息的字符串,下⽂会介绍。

*`file`:转换后的⽂件名。

*`sourceRoot`:转换前的⽂件所在的⽬录。如果与转换前的⽂件在同⼀⽬录,该项为空。

*`sourcesContent`:转换前⽂件的原始内容。

#5.1关于Sourcemap的版本

在2009年`Google`的⼀篇⽂章中,在介绍`CloureCompiler`时,`Google`也趁便推出了⼀款调试东西:`Firefox`插件`ClosureInspector`,以便利调试编译后代

>YoucanuthecompilerwithClosureInspector,aFirebugextensionthatmakesdebuggingtheobfuscatedcodealmostaasyasdebuggingthehuman-rea

2010年,在第⼆代即`ClosureCompilerSourceMap2.0`中,`SourceMap`招认了共同的`JSON`格式及其他标准,已⼏乎具有现在的雏形。最⼤的差异在于`ma

2011年,第三代即[**SourceMapRevision3Proposal**](/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#)出炉

`SourceMap`发展史的诙谐之处在于,它作为⼀款辅佐东西被开发出来。毕竟它辅佐的⽅针⽇渐式微,⽽它却成为了技能主体,被写进了浏览器中。

>SourceMapV1最初步⽣成的SourceMap⽂件⼤概有转化后⽂件的10倍⼤。SourceMapV2将之减少了50%,V3⼜在V2的基础上减少了50%。所以现在133k的⽂件

#5.2关于mappings属性

为了避免⼲扰,将案例改成如下不报错的情况:

```js

vara=1;

(a);

`

复制代码

打包编译的后⽂件:

/******/

(()=>{//webpackBootstrap

var__webpack_exports__={};

/*!**********************!*

!***./src/***!

**********************/

vara=1;

(a);

/******/

})();

//#sourceMappingURL=

复制代码

打包编译后的⽂件:

{

"version":3,

"sources":[

"webpack://learn-source-map/./src/"

],

"names":[],

"mappings":"AAAA;AACA,c",

"file":"",

"sourcesContent":[

"vara=1;

(a);"

],

"sourceRoot":""

}

复制代码

可以看到mappings属性的值是:AAAA;AACA,c,要想说清楚这个东西,需要先解释⼀下它的组成结构。这是⼀个字符串,它分成三

层:

第⼀层是⾏对应,以分号(;)表⽰,每个分号对应转换后源码的⼀⾏。所以,第⼀个分号前的内容,就对应源码的第⼀⾏,以此类

推。

第⼆层是位置对应,以逗号(,)表⽰,每个逗号对应转换后源码的⼀个位置。所以,第⼀个逗号前的内容,就对应该⾏源码的第⼀个

位置,以此类推。

第三层是位置转换,以VLQ编码

[16]

表⽰,代表该位置对应的转换前的源码位置。

在回到源代码,就可以分析出:

1.因为源代码中有两⾏,所以有⼀个分号,分号前后表⽰了第⼀⾏和第⼆⾏。即mappings中的AAAA和AACA,c。

2.分号后⾯表⽰第⼆⾏,也就是代码(a);可以拆分出两个位置,分别是console和log(a),所以存在⼀个逗号。即AACA,c中

的AACA和c。

总结,就是转换后的源码分成两⾏,第⼀⾏有⼀个位置,第⼆⾏有两个位置。

⾄于这个AAAA,AAcA等字母是怎么来的,可以参考阮⼀峰⽼师的JavaScriptSourceMap详解

[17]

有作详细的介绍。笔者⾃⼰的理解

是:

AAAA和AAcA以及c都是代表了位置,正常来说,每个位置最多由5个字母组成,5个字母的含义分别是:

第⼀位,表⽰这个位置在(转换后的代码的)的第⼏列。

第⼆位,表⽰这个位置属于sources属性中的哪⼀个⽂件。

第三位,表⽰这个位置属于转换前代码的第⼏⾏。

第四位,表⽰这个位置属于转换前代码的第⼏列。

第五位,表⽰这个位置属于names属性中的哪⼀个变量。

这⾥转换后最多只有4个字母,是因为没有names属性。

每⼀个位置都可以⽤VLQ编码

[18]

转换,形成⼀种映射关系。可以在这个⽹站

[19]

⾃⼰转换测试,将AAAA;AACA,c转换后的结果:

可以得到两组数据:

[0,0,0,0]

[0,0,1,0],[14]

复制代码

数字都是从0开始的,拿位置AAAA举例,转换后得到[0,0,0,0],所以代表的含义分别是;

1.压缩代码的第⼀列。

2.第⼀个源代码⽂件,即。

3.源代码的第⼀⾏。

4.源代码第⼀列

通过以上解析,我们就能知道源代码中vara=1;在打包后⽂件中,即的具体位置了。

六、Webpack中的SourceMap

上⽂介绍了SourceMap的作⽤,原理等。现在说⼀下打包⼯具WebPack中对SourceMap的应⽤,毕竟我们在开发中,都离不开它。

上⽂有说道,只需要在⽂件中配置devtool就可以使⽤SourceMap,这个devtool具体的值有哪些,可以参考

webpackdevtool

[20]

的介绍,官⽅罗列了20⼏种类型,我们当然不能全部都记住,可以记住⼏个关键的:

建议以下7种可选⽅案:

source-map:外部。可以查看错误代码准确信息和源代码的错误位置。

inline-source-map:内联。只⽣成⼀个内联SourceMap,可以查看错误代码准确信息和源代码的错误位置

hidden-source-map:外部。可以查看错误代码准确信息,但不能追踪源代码错误,只能提⽰到构建后代码的错误位置。

eval-source-map:内联。每⼀个⽂件都⽣成对应的SourceMap,都在eval中,可以查看错误代码准确信息和源代码的错误位置。

nosources-source-map:外部。可以查看错误代码错误原因,但不能查看错误代码准确信息,并且没有任何源代码信息。

cheap-source-map:外部。可以查看错误代码准确信息和源代码的错误位置,只能把错误精确到整⾏,忽略列。

cheap-module-source-map:外部。可以错误代码准确信息和源代码的错误位置,module会加⼊loader的SourceMap。

内联和外部的区别:

1.外部⽣成了⽂件(.map),内联没有。

2.内联构建速度更快。

以下通过具体的案例演⽰上⾯的7种类型:

⾸先,将案例改成报错状态,为了体现列的情况,将源代码修改成如下:

(sourcemap)

vara=1;

(a,b);//这⼀⾏肯定会报错

复制代码

6.1source-map

devtool:source-map

复制代码

编译后,可以查看错误代码准确信息和源代码的错误位置:

⽣成了.map⽂件:

6.2inline-source-map

devtool:inline-source-map

复制代码

编译后,可以查看错误代码准确信息和源代码的错误位置:

但是没有⽣成.map⽂件,⽽是以ba64的形式插⼊到sourceMappingURL中:

6.3hidden-source-map

devtool:hidden-source-map

复制代码

编译后,可以查看错误代码准确信息,但是⽆法查看源代码的位置:

⽣成了.map⽂件:

6.4eval-source-map

devtool:eval-source-map

复制代码

编译后,可以查看错误代码准确信息和源代码的错误位置:

但是没有⽣成.map⽂件,⽽是在eval函数中,包括sourceMappingURL:

6.5nosources-source-map

devtool:nosources-source-map

复制代码

编译后,可以查看⽆法查看错误代码的准确位置和源代码的错误位置,只能提⽰错误原因:

⽣成了.map⽂件:

6.6cheap-source-map

devtool:cheap-source-map

复制代码

编译后,可以查看错误代码准确信息和源代码的错误位置,但是忽略了具体的列(因为是b导致报错):

⽣成了.map⽂件:

6.7cheap-module-source-map

因为需要module,所以案例中增加loader:

module:{

rules:[{

test:/.css$/,

u:[

//style-loader:创建style标签,将js中的样式资源插⼊进去,添加到head中⽣效

style-loader,

//css-loader:将css⽂件变成commonjs模块加载到js中,⾥⾯内容是样式字符串

css-loader

]

}]

}

复制代码

在src⽬录下新建⽂件,添加样式代码:

body{

margin:0;

padding:0;

height:100%;

background-color:pink;

}

复制代码

然后在src/中引⼊:

//引⼊

import./;

(sourcemap)

vara=1;

(a,b);//这⼀⾏肯定会报错

复制代码

修改devtool:

devtool:cheap-module-source-map

复制代码

打包后,打开浏览器,样式⽣效,说明loader引⼊成功。可以查看错误代码准确信息和源代码的错误位置,但是忽略了具体的列(因为是

b导致报错):

⽣成了.map⽂件,同时,将loader的信息也⼀起打包进来:

6.8总结

(1)开发环境:需要考虑速度快,调试更友好

速度快(eval>inline>cheap>...)

-cheap-souce-map

-source-map

调试更友好

-map

-module-souce-map

-souce-map

最终得出最好的两种⽅案-->eval-source-map(完整度⾼,内联速度快)/eval-cheap-module-souce-map(错误提⽰忽略列但是包含

其他信息,内联速度快)

(2)⽣产环境:需要考虑源代码要不要隐藏,调试要不要更友好

内联会让代码体积变⼤,所以在⽣产环境不⽤内联

隐藏源代码

ces-source-map全部隐藏(打包后的代码与源代码)

-source-map只隐藏源代码,会提⽰构建后代码错误信息

最终得出最好的两种⽅案-->source-map(最完整)/cheap-module-souce-map(错误提⽰⼀整⾏忽略列)

七、总结

SourceMap是我们⽇常开发过程中必不可少的,它可以帮助我们调试,定位错误。尽管它涉及⾮常多的知识点,例如:VLQ[21]

ba64

[22]

等,但是我们核⼼关注的是它的⼯作原理,以及在打包⼯具中,如webpack等对SourceMap的应⽤。

SourceMap⾮常强⼤,不仅在应⽤于⽇常开发,还可以做更多的事情,如性能异常监控平台。⽐如FunDebug[23]

这个⽹站就是通过

SourceMap还原⽣产环境中的压缩代码,提供完整的堆栈信息,准确定位出错误源码,帮助⽤户快速修复Bug,像这样的案例还有许多。

总之,学习SourceMap是⾮常有必要的。

⼋、参考

IntroductiontoJavaScriptSourceMaps

[24]

MDN

[25]

JavaScriptSourceMap详解

[26]

VLQ

[27]

ba64

[28]

ba64vlq

[29]

FunDebug

[30]

绝了,没想到⼀个sourcemap居然涉及到那么多知识盲区

[31]

谈谈我是如何获得知乎的前端源码的

[32]

关于本⽂

来源:IDuxFE

TheEnd

“在看”吗?在看就点⼀下吧

本文发布于:2022-12-28 13:00:24,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/fanwen/fan/90/46777.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

上一篇:pyrex
下一篇:eclair
标签:grunt
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图