UglifyJS是基於 NodeJS 的Javascript語法解析/壓縮/格式化工具,它支持任何CommonJS模塊系統的Javascript平台(實現自己的CommonJS平台也非難事)。 UglifyJS通過解析重新生成JS代碼的語法樹,你可以通過AST以了解更多代碼情況,或者自己來做一個不同的實現。UglifyJS解析器是在 parse-js.js 中實現的,它是非常優秀的 parse。
非安全轉換
UglifyJS是基於 NodeJS 的Javascript語法解析/壓縮/格式化工具,它支持任何CommonJS模塊系統的Javascript平台(實現自己的CommonJS平台也非難事)。
UglifyJS通過解析重新生成JS代碼的語法樹,你可以通過AST以了解更多代碼情況,或者自己來做一個不同的實現。UglifyJS解析器是在 parse-js.js 中實現的,它是非常優秀的 parse-js Common Lisp Library 的一部分。
(如果你正在查找UglifyJS的Common Lisp版本,點擊 這里 )
UglifyJS的另一個重要部分是在 process.js 實現的,它用於檢查並實現解析生成的AST:
- 通過AST進行Javascript代碼的重新生成 :如果你想格式化已經被壓縮過的代碼,可以選擇縮進參數。你也可以打印出無空白(whitespace)的AST,以達到壓縮的目的。
- 縮短變量名 :UglifyJS通過分析代碼並生成新的變量名稱,依賴於作用域,這些名稱通常被簡化為單一字符,並能足夠智能的處理全局變量,或者eval()調用及with{}塊。換句話說,如果在某個作用域內使用了eval()或with{},那么該作用域的所有變量及其父作用域的變量都不會被重新命名,並且所有指向這類變量的引用也不會被改變。
- 以下是一些優化規則會讓代碼更簡潔更高效 :
- foo["bar"] ==> foo.bar
- 刪除塊標記{}
- 合並變量聲明: var a = 10; var b = 20; ==> var a=10,b=20;
- 計算簡單的常量表達式:1 + 2 * 3 ==> 7. UglifyJS只替換計算結果比實際表達式字節更少的情況;比如 1/3 結果為 0.333333333333,因此不會被替換。
- 連續的語句塊會被合並為一個序列;大多情況下,這將保留一個語句,接下來塊括號可以被移除。
- IF語句的優化 :
- if (foo) bar(); else baz(); ==> foo?bar():baz();
- if (!foo) bar(); else baz(); ==> foo?baz():bar();
- if (foo) bar(); ==> foo&&bar();
- if (!foo) bar(); ==> foo||bar();
- if (foo) return bar(); else return baz(); ==> return foo?bar():baz();
- if (foo) return bar(); else something(); ==> {if(foo)return bar();something()}
- 移除不會被用到的代碼並會給出警告。
非安全轉換
UglifyJS在保留語義的同時會盡量提高壓縮比率,如果經過UglifyJS處理后你的代碼邏輯失效了,或對UglifyJS的優化實現有更好的想法,都可有直接聯系作者。
涉及到全局數組構造函數的調用
這時會進行如下轉換:
newArray(1,2,3,4)=>[1,2,3,4]Array(a, b, c)=>[a,b,c]newArray(5)=>Array(5)newArray(a)=>Array(a)
在Array沒有被重新定義之前,這些轉換是安全的。UglifyJS也會對經過用戶本地或全局重定義的Array進行處理,但CSSer建議還是不要這么做:
// case 1. 全局聲明varArray;newArray(1,2, www.csser.com);Array(a, b);// 或者后聲明newArray(1,2,3);varArray;// 或者定義為函數newArray(1,2,3);functionArray(){...}// case 2. 在函數內部聲明(function(){ a =newArray(1,2,3); b =Array(5,6);varArray;})();// 或者(function(Array){returnArray(5,6,7);})();// 或者(function(){returnnewArray(1,2,3,4);functionArray(){...}})();
// 等等.
安裝
通過NPM安裝
UglifyJS已經可以通過NPM進行安裝:
npm install uglify-js
通過GitHub安裝最新版本
## 克隆倉庫 mkdir -p /where/you/wanna/put/it cd /where/you/wanna/put/it git clone git://github.com/mishoo/UglifyJS.git## 讓uglify模塊對NodeJS有效 mkdir -p ~/.node_libraries/ cd ~/.node_libraries/ ln -s /where/you/wanna/put/it/UglifyJS/uglify-js.js ## 支持命令行的方式調用 mkdir -p ~/bin cd ~/bin ln -s /where/you/wanna/put/it/UglifyJS/bin/uglifyjs # (然后將 ~/bin 增加到 $PATH)
如何使用
UglifyJS提供了命令行工具,支持shell腳本的操作需要:
uglifyjs [選項...][文件名]
最后一個參數是要處理的JS文件名,如果不指定,則從標准輸入(STDIN)讀取。
支持的選項 :
- -b 或 --beautify - 輸出格式化代碼,當傳入該參數,下面的附加選項用於更美觀的控制格式化:
- -i N 或 --indent N - 縮進級別(空格數量)
- -q 或 --quote-keys - 是否用引號引起字符串對象的鍵(默認只會引起不能被正確標志的鍵名)
- --ascii -默認 UglifyJS 不處理字符編碼而直接輸出 Unicode 字符,通過傳入該參數將非ASCII編碼的字符轉化為\cXXXX的序列(輸出總按照UTF8編碼,但傳入該選項能得到ASCII編碼的輸出)。
- -nm 或 --no-mangle - 不改變變量名稱
- -ns 或 --no-squeeze - 不調用 ast_squeeze() 函數(該函數會做多種優化使得結果更小,可讀性略有降低)
- -mt 或 --mangle-toplevel - 在頂級作用域打亂變量名稱(默認不開啟)
- --no-seqs - 當調用 ast_squeeze() 將會合並多個語句塊為一個語句塊,如 "a=10; b=20; foo()" 將被轉換為 "a=10,b=20,foo()"
- --no-dead-code - 默認 UglifyJS 將會刪除不被用到的代碼,傳入該參數禁用此功能。
- -nc 或 --no-copyright - 默認 uglifyjs 會在輸出后的代碼中添加版權信息等注釋代碼,傳入該參數禁用此功能。
- -o 文件名 或 --output 文件名 - 指定輸出文件名,如果不指定,則打印到標准輸出(STDOUT)
- --overwrite - 如果傳入的JS代碼來自文件而不是標准輸入,傳入該參數,輸出會覆蓋該文件。
- --ast - 傳入該參數會得到抽象的語法樹而不是Javascript,對調試或了解內部代碼很有用。
- -v 或 --verbose - 在標准錯誤輸出一些信息(目前的版本僅輸出操作用時)
- --extra - 開啟附加優化,這些優化並未得到全面的測試。
- --unsafe - 開啟其他附加優化,這些優化已知在特定情況下並不安全,目前僅支持:
- foo.toString() ==> foo+””
- --max-line-len (默認32K字節) - 在32K字節出增加換行符,傳入0禁用此功能。
- --reserved-names - 一些類庫會依賴一些變量,該參數指定的名稱不會被混淆掉,多個用逗號隔開。
API
要想在Javascript中使用UglifyJS類庫,參考下面的示例(以NodeJS為例):
var jsp =require("uglify-js").parser;var pro =require("uglify-js").uglify;var orig_code ="Javascript代碼";var ast = jsp.parse(orig_code);// 解析代碼返回初始的AST ast = pro.ast_mangle(ast);// 獲取變量名稱打亂的AST ast = pro.ast_squeeze(ast);// 獲取經過壓縮優化的ASTvar final_code = pro.gen_code(ast);// 壓縮后的代碼
上面的代碼會立刻進行代碼的全面壓縮,正如你所看到的,這里經歷了一系列的步驟,你可有省略某些步驟以滿足自己的需求。
這里的函數有一些參數,我們做些介紹:
- jsp.parse(code, strict_semicolons) - 解析JS代碼並返回AST。strict_semicolons是可選的,默認為false,當傳入true,解析器會在預期為分號而實際沒找到的情況下拋出錯誤。對於大多數JS代碼我們不需要那么做,但嚴格約束代碼很有益處。
- pro.ast_mangle(ast, options) - 返回經過變量和函數名稱混淆的AST,它支持以下選項:
- toplevel - 混淆頂級作用域的變量和函數名稱(默認不開啟)。
- except - 指定不被壓縮的名稱的數組
- pro.ast_squeeze(ast, options) - 開啟深度優化以降低代碼尺寸,返回新的AST,選項可以是一個hash,支持的參數有:
- make_seqs (默認true) 將多個語句塊合並為一個。
- dead_code (默認true) 將刪除不被使用的代碼。
- pro.gen_code(ast, options) - 通過AST生成JS代碼。默認輸出壓縮代碼,但可以通過調整選項參數獲得格式化的輸出。選項是可選的,如果傳入必須為對象,支持以下選項:
- beautify: false - 如果希望得到格式化的輸出,傳入true
- indent_start: 0 (僅當beautify為true時有效) - 初始縮進空格
- indent_level: 4 (僅當beautify為true時有效) - 縮進級別,空格數量
- quote_keys: false - 傳入true將會用引號引起所有文本對象的key
- space_colon: false (僅當beautify為true時有效) - 是否在冒號前保留空格
- ascii_only: false - 傳入true則將編碼非ASCII字符到\uXXXX
結語
UglifyJS在語法上與Google壓縮相似,在性能上是一流的,所以建議多多實踐!