用UglifyJS2合并压缩混淆JS代码

前言

做Web前端开发,总是要考虑页面的打开速度,如果文件数量越少、文件长度越小,就可以直接的提升网页的访问速度。

但在开发的时候,为了保证代码的可读性,我们写的程序文件会很多而且很大,这样就与部署的要求发生背离,通过UglifyJS2这个工具,我们可以在开发完成时,对代码文件进行 合并、混淆、压缩 等的操作,达到最优的访问性能。

目录

  1. UglifyJS介绍
  2. UglifyJS2介绍
  3. UglifyJS2安装
  4. UglifyJS2命令操作
  5. UglifyJS2的API使用

1. UglifyJS介绍

开始UglifyJS2介绍之前,我们先要说一下UglifyJS。UglifyJS是UglifyJS2的前身,是一个Javascript开发的通用的语法分析、代码压缩、代码优化的一个工具包。UglifyJS是基于Nodejs环境开发,支持CommonJS模块系统的任意的Javascript平台。

UglifyJS的实现主要分为2部分:

  • 生成JS代码的抽象语法树(AST),通过parse-js.js完成。
  • 遍历AST语法树,做各种操作,比如自动缩进、缩短变量名、删除块括号{}、去空格、常量表达式、连续变量声明、语块合并、去掉无法访问的代码等,通过process.js完成。

2. UglifyJS2介绍

UglifyJS2是作者对UglifyJS的重写,是完全的重写,而不仅仅是升级。从UglifyJS2官司方网页介绍看,UglifyJS2把整个的JS压缩过程,做了更进一步的细化。

上述所有的功能代码API是​​在6500行的左右,比其他的相同功能的开发包都要小。作者还提供了一个在线版本UglifyJS2的JS压缩工具,http://lisperator.net/uglifyjs/,大家可以测试一下。

3. UglifyJS2安装

系统环境:

  • win7 64bit
  • Nodejs:v0.10.5
  • Npm:1.2.19

UglifyJS2的安装非常简单,和Nodejs的其他包一样,全局安装使用如下命令。

 

[plain] view plain copy
 
  1. npm install uglify-js -g  


也可以通过github下载源代码安装。

 

[plain] view plain copy
 
  1. git clone git://github.com/mishoo/UglifyJS2.git  
  2. cd UglifyJS2  

 

我们在使用UglifyJS2的时候主要有2种方式,一种是通过命令行操作,对指定的JS文件进行压缩;另一种是通过程序的API调用,对文件或内存中的JS代码进行压缩。下面我将分两种情况进行介绍。

4. UglifyJS2命令操作

在全局安装好UglifyJS2以后,我们就可以使用UglifyJS2的命令了。

打印uglifyjs命令行的帮助信息,会打出很长一段说明。

[plain] view plain copy
 
  1. D:\workspace\javascript\nodejs-uglifyJS2>uglifyjs -h  
  2. D:\toolkit\nodejs\\node.exe D:\toolkit\nodejs\node_modules\uglify-js\bin\uglifyjs input1.js [input2.js ...] [options]  
  3. Use a single dash to read input from the standard input.  
  4.   
  5. NOTE: by default there is no mangling/compression.  
  6. Without [options] it will simply parse input files and dump the AST  
  7. with whitespace and comments discarded.  To achieve compression and  
  8. mangling you need to use `-c` and `-m`.  
  9.   
  10. Options:  
  11.   --source-map                  Specify an output file where to generate source  
  12.                                 map.                                    [string]  
  13.   --source-map-root             The path to the original source to be included  
  14.                                 in the source map.                      [string]  
  15.   --source-map-url              The path to the source map to be added in //#  
  16.                                 sourceMappingURL.  Defaults to the value passed  
  17.                                 with --source-map.                      [string]  
  18.   --source-map-include-sources  Pass this flag if you want to include the  
  19.                                 content of source files in the source map as  
  20.                                 sourcesContent property.               [boolean]  
  21.   --in-source-map               Input source map, useful if you're compressing  
  22.                                 JS that was generated from some other original  
  23.                                 code.  
  24.   --screw-ie8                   Pass this flag if you don't care about full  
  25.                                 compliance with Internet Explorer 6-8 quirks  
  26.                                 (by default UglifyJS will try to be IE-proof).  
  27.                                                                        [boolean]  
  28.   --expr                        Parse a single expression, rather than a  
  29.                                 program (for parsing JSON)             [boolean]  
  30.   -p, --prefix                  Skip prefix for original filenames that appear  
  31.                                 in source maps. For example -p 3 will drop 3  
  32.                                 directories from file names and ensure they are  
  33.                                 relative paths. You can also specify -p  
  34.                                 relative, which will make UglifyJS figure out  
  35.                                 itself the relative paths between original  
  36.                                 sources, the source map and the output file.  
  37.                                                                         [string]  
  38.   -o, --output                  Output file (default STDOUT).  
  39.   -b, --beautify                Beautify output/specify output options.  
  40.                                                                         [string]  
  41.   -m, --mangle                  Mangle names/pass mangler options.      [string]  
  42.   -r, --reserved                Reserved names to exclude from mangling.  
  43.   -c, --compress                Enable compressor/pass compressor options. Pass  
  44.                                 options like -c  
  45.                                 hoist_vars=false,if_return=false. Use -c with  
  46.                                 no argument to use the default compression  
  47.                                 options.                                [string]  
  48.   -d, --define                  Global definitions                      [string]  
  49.   -e, --enclose                 Embed everything in a big function, with a  
  50.                                 configurable parameter/argument list.   [string]  
  51.   --comments                    Preserve copyright comments in the output. By  
  52.                                 default this works like Google Closure, keeping  
  53.                                 JSDoc-style comments that contain "@license" or  
  54.                                 "@preserve". You can optionally pass one of the  
  55.                                 following arguments to this flag:  
  56.                                 - "all" to keep all comments  
  57.                                 - a valid JS regexp (needs to start with a  
  58.                                 slash) to keep only comments that match.  
  59.                                 Note that currently not *all* comments can be  
  60.                                 kept when compression is on, because of dead  
  61.                                 code removal or cascading statements into  
  62.                                 sequences.                              [string]  
  63.   --preamble                    Preamble to prepend to the output.  You can use  
  64.                                 this to insert a comment, for example for  
  65.                                 licensing information.  This will not be  
  66.                                 parsed, but the source map will adjust for its  
  67.                                 presence.  
  68.   --stats                       Display operations run time on STDERR.  
  69.                                                                        [boolean]  
  70.   --acorn                       Use Acorn for parsing.                 [boolean]  
  71.   --spidermonkey                Assume input files are SpiderMonkey AST format  
  72.                                 (as JSON).                             [boolean]  
  73.   --self                        Build itself (UglifyJS2) as a library (implies  
  74.                                 --wrap=UglifyJS --export-all)          [boolean]  
  75.   --wrap                        Embed everything in a big function, making the  
  76.                                 “exports” and “global” variables available. You  
  77.                                 need to pass an argument to this option to  
  78.                                 specify the name that your module will take  
  79.                                 when included in, say, a browser.       [string]  
  80.   --export-all                  Only used when --wrap, this tells UglifyJS to  
  81.                                 add code to automatically export all globals.  
  82.                                                                        [boolean]  
  83.   --lint                        Display some scope warnings            [boolean]  
  84.   -v, --verbose                 Verbose                                [boolean]  
  85.   -V, --version                 Print version number and exit.         [boolean]  
  86.   --noerr                       Don't throw an error for unknown options in -c,  
  87.                                 -b or -m.                              [boolean]  

 

对命令参数进行解释:

  • –source-map [string],生成source map文件。
  • –source-map-root [string], 指定生成source map的源文件位置。
  • –source-map-url [string], 指定source map的网站访问地址。
  • –source-map-include-sources,设置源文件被包含到source map中。
  • –in-source-map,自定义source map,用于其他工具生成的source map。
  • –screw-ie8, 用于生成完全兼容IE6-8的代码。
  • –expr, 解析一个表达式或JSON。
  • -p, –prefix [string], 跳过原始文件名的前缀部分,用于指定源文件、source map和输出文件的相对路径。
  • -o, –output [string], 输出到文件。
  • -b, –beautify [string], 输出带格式化的文件。
  • -m, –mangle [string], 输出变量名替换后的文件。
  • -r, –reserved [string], 保留变量名,排除mangle过程。
  • -c, –compress [string], 输出压缩后的文件。
  • -d, –define [string], 全局定义。
  • -e, –enclose [string], 把所有代码合并到一个函数中,并提供一个可配置的参数列表。
  • –comments [string], 增加注释参数,如@license、@preserve。
  • –preamble [string], 增加注释描述。
  • –stats, 显示运行状态。
  • –acorn, 用Acorn做解析。
  • –spidermonkey, 解析SpiderMonkey格式的文件,如JSON。
  • –self, 把UglifyJS2做为依赖库一起打包。
  • –wrap, 把所有代码合并到一个函数中。
  • –export-all, 和–wrap一起使用,自动输出到全局环境。
  • –lint, 显示环境的异常信息。
  • -v, –verbose, 打印运行日志详细。
  • -V, –version, 打印版本号。
  • –noerr, 忽略错误命令行参数。

通过对命令行各种参数的解释,我们基本上知道了这些参数都是干什么的了,下面我就试一下。

写2个简单地JS文件,demo.js, main.js。

[plain] view plain copy
 
  1. ~ vi D:\workspace\javascript\nodejs-uglifyJS2\demo.js  
  2.   
  3. 'use strict';  
  4.   
  5. function hello(name){  
  6.     if(name==='fens.me'){  
  7.         return "Long time no see, "+name;  
  8.     }  
  9.     return "hello "+name;  
  10. }  
  11.   
  12. console.log(hello('Conan'));  
  13. console.log(hello('fens.me'));  


main.js

 

[plain] view plain copy
 
  1. ~ vi D:\workspace\javascript\nodejs-uglifyJS2\main.js  
  2.   
  3. 'use strict';  
  4.   
  5. function book(){  
  6.     return [  
  7.         {head:'前言',page:'/views/tpl/book-r1/preface.html',active:false},  
  8.         {head:'目录',page:'/views/tpl/book-r1/contents.html',active:true},  
  9.         {head:'代码',page:'/views/tpl/book-r1/code.html',active:false},  
  10.         {head:'试读',page:'/views/tpl/book-r1/sample.html',active:false},  
  11.         {head:'勘误',page:'/views/tpl/book-r1/mistake.html',active:false}  
  12.     ];  
  13. }  
  14.   
  15. var tab=function(arr,idx){  
  16.     for(var i=0;i<arr.length;i++){  
  17.         arr[i].active = (idx==i?true:false);  
  18.     }  
  19.     return arr;  
  20. }  
  21.   
  22. console.log(tab(book(),3));  


接下来,用UglifyJS2命令进行操作,合并两个文件,对变量名用单字母替换,进行压缩,所有代码合并到一个函数,生成source map,指定source map来源网站。

 

[plain] view plain copy
 
  1. D:\workspace\javascript\nodejs-uglifyJS2>uglifyjs main.js demo.js -o foo.min.js --source-map foo.min.js.map --source-map-root http://onbook.me -p 5 -c -m --wrap --export-all  

 

在当前目录生成了2个新文件:foo.min.js.map, foo.min.js,分别查看这两个文件。

foo.min.js

[plain] view plain copy
 
  1. !function(e,t){"use strict";function o(){return[{head:"前言",page:"/views/tpl/book-r1/preface.html",active:!1},{head:"目录",page:"/views/tpl/book-r1/contents.html",active:!0},{head:"代码",page:"/views/tpl/book-r1/code.html",active:!1},{head:"试读",page:"/views/tpl/book-r1/sample.html",active:!1},{head:"勘误",page:"/views/tpl/book-r1/mistake.html",active:!1}]}function n(e){return"fens.me"===e?"Long time no see, "+e:"hello "+e}t["true"]=e,console.log(a(o(),3));var a=function(e,t){for(var o=0;o  


foo.min.js.map

[plain] view plain copy
 
  1. {"version":3,"file":"foo.min.js","sources":["?"],"names":["exports","global","book","head","page","active","hello","name","console","log","tab","arr","idx","i","length","this"],"mappings":"CAAC,SAASA,EAASC,GAAnB,YAEA,SAASC,KACL,QACKC,KAAK,KAAKC,KAAK,kCAAkCC,QAAO,IACxDF,KAAK,KAAKC,KAAK,mCAAmCC,QAAO,IACzDF,KAAK,KAAKC,KAAK,+BAA+BC,QAAO,IACrDF,KAAK,KAAKC,KAAK,iCAAiCC,QAAO,IACvDF,KAAK,KAAKC,KAAK,kCAAkCC,QAAO,IANjE,QAASC,GAAMC,GACd,MAAU,YAAPA,EACK,qBAAqBA,EAEtB,SAASA,EANWN,EAAO,QAAUD,EAY7CQ,QAAQC,IAAIC,EAAIR,IAAO,GADvB,IAAIQ,GAAI,SAASC,EAAIC,GACpB,IAAI,GAAIC,GAAE,EAAEA,EAAEF,EAAIG,OAAOD,IACxBF,EAAIE,GAAGR,OAAUO,GAAKC,GAAE,GAAK,CAE9B,OAAOF,GAGRH,SAAQC,IAAIH,EAAM,UAClBE,QAAQC,IAAIH,EAAM,mBAjBTJ,UAAAI,QASLI,MAX8E,WAAW,MAAOK","sourceRoot":"http://onbook.me"}  

 

通过一条简单的命令,就实现了对JS代码的合并、压缩等的操作,确实非常方便。

下载jquery-2.1.1.js文件自己压缩,并与官方的压缩文件进行对比。

[plain] view plain copy
 
  1. # 下载  
  2. ~ wget http://code.jquery.com/jquery-2.1.1.js  
  3. ~ wget http://code.jquery.com/jquery-2.1.1.min.js  
  4.   
  5. # 压缩  
  6. ~ uglifyjs jquery-2.1.1.js -o jquery-2.1.1.min.uglifyjs2.js -p 5 -c -m  
  7.   
  8. # 比较3个文件大小  
  9. ~ ls -l  
  10. -rwx------  1 4294967295 mkpasswd 247351 Jul  6 16:26 jquery-2.1.1.js  
  11. -rwx------  1 4294967295 mkpasswd  84245 Jul  6 16:32 jquery-2.1.1.min.js  
  12. -rwx------  1 4294967295 mkpasswd  84113 Jul  6 16:28 jquery-2.1.1.min.uglifyjs2.js  

我在本地压缩的文件jquery-2.1.1.min.uglifyjs2.js,与jquery官司网下载的压缩文件jquery-2.1.1.min.js大小差不多,都在84KB左右。

5. UglifyJS2的API使用

另一种使用方式是,把UglifyJS2包放到程序中,通过API对JS文件或JS代码进行压缩。首先,新建一个NPM项目文件package.json,然后在是下载UglifyJS2依赖包。

新建文件package.json

[plain] view plain copy
 
  1. ~ vi D:\workspace\javascript\nodejs-uglifyJS2\package.json  
  2.   
  3.   
  4. {  
  5.   "name": "nodejs-uglifyjs2",  
  6.   "version": "0.0.1",  
  7.   "description": "uglifyjs2",  
  8.   "author": "Conan Zhang ",  
  9.   "dependencies": {  
  10.   }  
  11. }  

下载UglifyJS2依赖包

[plain] view plain copy
 
  1. D:\workspace\javascript\nodejs-uglifyJS2>npm install uglify-js --save  
  2. npm WARN package.json nodejs-uglifyjs2@0.0.1 No readme data!  
  3. npm http GET https://registry.npmjs.org/uglify-js  
  4. npm http 304 https://registry.npmjs.org/uglify-js  
  5. npm http GET https://registry.npmjs.org/async  
  6. npm http GET https://registry.npmjs.org/source-map  
  7. npm http GET https://registry.npmjs.org/optimist  
  8. npm http GET https://registry.npmjs.org/uglify-to-browserify  
  9. npm http 304 https://registry.npmjs.org/uglify-to-browserify  
  10. npm http 304 https://registry.npmjs.org/optimist  
  11. npm http 304 https://registry.npmjs.org/async  
  12. npm http 304 https://registry.npmjs.org/source-map  
  13. npm http GET https://registry.npmjs.org/wordwrap  
  14. npm http GET https://registry.npmjs.org/amdefine  
  15. npm http 304 https://registry.npmjs.org/wordwrap  
  16. npm http 304 https://registry.npmjs.org/amdefine  
  17. uglify-js@2.4.14 node_modules\uglify-js  
  18. ├── uglify-to-browserify@1.0.2  
  19. ├── async@0.2.10  
  20. ├── optimist@0.3.7 (wordwrap@0.0.2)  
  21. └── source-map@0.1.34 (amdefine@0.1.0)  


我们新建一个文件uglify2.js,用于编写程序。

[plain] view plain copy
 
  1. ~ vi D:\workspace\javascript\nodejs-uglifyJS2\uglify2.js  
  2.   
  3. 'use strict';  
  4.   
  5. var UglifyJS = require('uglify-js');  
  6.   
  7. //代码压缩  
  8. var result = UglifyJS.minify("var b = function () {};", {fromString: true});  
  9. console.log("\n===========================");  
  10. console.log(result);  
  11.   
  12. //文件压缩  
  13. result = UglifyJS.minify(["demo.js"]);  
  14. console.log("\n===========================");  
  15. console.log(result.code);  
  16.   
  17. //多文件压缩,指定source map和网站来源  
  18. result = UglifyJS.minify(["main.js","demo.js"],{  
  19.     outSourceMap: "out.js.map",  
  20.     sourceRoot: "http://onbook.me",  
  21.     mangle:true  
  22. });  
  23. console.log("\n===========================");  
  24. console.log(result.code);  
  25. console.log(result.map);  


程序输出:

[plain] view plain copy
 
  1. D:\workspace\javascript\nodejs-uglifyJS2>node uglify2.js  
  2.   
  3. ===========================  
  4. { code: 'var b=function(){};', map: 'null' }  
  5.   
  6. ===========================  
  7. "use strict";function hello(e){return"fens.me"===e?"Long time no see, "+e:"hello "+e}var tab=function(e,o){for(var n=0;n  
  8. <e.length;n++)e[n].active=o==n?!0:!1;return e};console.log(hello("Conan")),console.log(hello("fens.me"));  
  9.   
  10. ===========================  
  11. "use strict";function book(){return[{head:"前言",page:"/views/tpl/book-r1/preface.html",active:!1},{head:"目录",page:"/v  
  12. iews/tpl/book-r1/contents.html",active:!0},{head:"代码",page:"/views/tpl/book-r1/code.html",active:!1},{head:"试读",page  
  13. :"/views/tpl/book-r1/sample.html",active:!1},{head:"勘误",page:"/views/tpl/book-r1/mistake.html",active:!1}]}function he  
  14. llo(e){return"fens.me"===e?"Long time no see, "+e:"hello "+e}console.log(tab(book(),3));var tab=function(e,o){for(var t=  
  15. 0;t<e.length;t++)e[t].active=o==t?!0:!1;return e};console.log(hello("Conan")),console.log(hello("fens.me"));  
  16. //# sourceMappingURL=out.js.map  
  17. {"version":3,"file":"out.js.map","sources":["main.js","demo.js"],"names":["book","head","page","active","hello","name","  
  18. console","log","tab","arr","idx","i","length"],"mappings":"AAAA,YAEA,SAASA,QACL,QACKC,KAAK,KAAKC,KAAK,kCAAkCC,QAAO,IACxD  
  19. F,KAAK,KAAKC,KAAK,mCAAmCC,QAAO,IACzDF,KAAK,KAAKC,KAAK,+BAA+BC,QAAO,IACrDF,KAAK,KAAKC,KAAK,iCAAiCC,QAAO,IACvDF,KAAK,KAAKC  
  20. ,KAAK,kCAAkCC,QAAO,ICNjE,QAASC,OAAMC,GACd,MAAU,YAAPA,EACK,qBAAqBA,EAEtB,SAASA,EDMjBC,QAAQC,IAAIC,IAAIR,OAAO,GCDvB,IAAIQ,  
  21. KAAI,SAASC,EAAIC,GACpB,IAAI,GAAIC,GAAE,EAAEA,EAAEF,EAAIG,OAAOD,IACxBF,EAAIE,GAAGR,OAAUO,GAAKC,GAAE,GAAK,CAE9B,OAAOF,GAGR  
  22. H,SAAQC,IAAIH,MAAM,UAClBE,QAAQC,IAAIH,MAAM","sourceRoot":"http://onbook.me"}  



我们看到用操作uglifyJS2包的API,还是挺简单的,如果对AST树有遍历需求,API提供了非常实用的函数支持。

不过我在测试API过程中,发现有2个问题。

  • 通过API设置mangle选项,但输出没有效果。
  • 没有--wrap和--export-all 命令行参数对应的API。

通过本文的介绍,我们基本上了解了uglifyJS2包的功能和使用方法,然后就可以放心大胆地对JS代码进行压缩了。在实际的前端项目中,一般不用自己配置uglifyJS2包,而是通过grunt来调用uglifyJS2进行代码发布前的压缩,关于grunt使用,请参考文章:grunt让Nodejs规范起来

posted on 2018-06-13 15:23  &大飞  阅读(3210)  评论(0编辑  收藏  举报

导航