【内推字节】欢迎简历chengxinsong@bytedance.com

用UglifyJS解析/压缩/格式化js代码

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 - 如果希望得到格式化的输出,传入trueindent_start: 0 (仅当beautify为true时有效) - 初始缩进空格indent_level: 4 (仅当beautify为true时有效) - 缩进级别,空格数量quote_keys: false - 传入true将会用引号引起所有文本对象的keyspace_colon: false (仅当beautify为true时有效) - 是否在冒号前保留空格ascii_only: false - 传入true则将编码非ASCII字符到\uXXXX 

结语

UglifyJS在语法上与Google压缩相似,在性能上是一流的,所以建议多多实践!


文章转载:原创
感谢你的阅读,本文由 sau交流学习社区 版权所有。
如若转载,请注明出处:sau交流学习社区-power by saucxs(程新松)(/page/529.html)
交流咨询
    官方QQ群
    群号663940201,欢迎加入!
    sau交流学习社区交流群

微信群
欢迎加入微信群
微信公众号
欢迎关注微信公众号

图文推荐

微信群
saucxs聊天机器人
saucxs
hi ,欢迎来到sau交流学习社区,欢迎与我聊天,问我问题哦!