注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

学会珍惜

You've been saying for the longest time

 
 
 

日志

 
 

用UglifyJS解析/压缩/格式化你的Javascript  

2012-12-29 17:11:35|  分类: 持续集成 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
编辑自:CSSer.com

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的优化实现有更好的想法,都可有直接联系作者。

涉及到全局数组构造函数的调用

这时会进行如下转换:

new Array(1, 2, 3, 4)  => [1,2,3,4] Array(a, b, c)         => [a,b,c] new Array(5)           => Array(5) new Array(a)           => Array(a)

在Array没有被重新定义之前,这些转换是安全的。UglifyJS也会对经过用户本地或全局重定义的Array进行处理,但CSSer建议还是不要这么做:

// case 1.  全局声明   var Array;   new Array(1, 2, www.csser.com);   Array(a, b);    // 或者后声明   new Array(1, 2, 3);   var Array;    // 或者定义为函数   new Array(1, 2, 3);   function Array() { ... }  // case 2. 在函数内部声明   (function(){     a = new Array(1, 2, 3);     b = Array(5, 6);     var Array;   })();    // 或者   (function(Array){     return Array(5, 6, 7);   })();    // 或者   (function(){     return new Array(1, 2, 3, 4);     function Array() { ... }   })();

// 等等.

安装

通过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); // 获取经过压缩优化的AST var 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压缩相似,在性能上是一流的,所以建议多多实践!


  评论这张
 
阅读(309)| 评论(0)
推荐

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017