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压缩相似,在性能上是一流的,所以建议多多实践!