用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規范起來


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM