Miscellaneous

保留版权告示或其他注释

你可以传入--comments让输出文件中保留某些注释。默认时会保留JSDoc-style的注释(包含”@preserve”,”@license” 或 “@cc_on”(为IE所编译))。你可以传入--comments all来保留全部注释,或者传一个合法的正则来保留那些匹配到的注释。例如--comments /^!/会保留/*! Copyright Notice */这样的注释。

注意,无论如何,总会有些注释在某些情况下会丢失。例如:

  1. function f() {
  2. /** @preserve Foo Bar */
  3. function g() {
  4. // this function is never called
  5. }
  6. return something();
  7. }

即使里面带有”@preserve”,注释依然会被丢弃。因为内部的函数g(注释所依附的抽象语法树节点)没有被引用、会被压缩器干掉。

书写版权信息(或其他需要在输出文件中保留的信息)的最安全位置是全局节点。

unsafe``compress配置

The unsafe compress option

在某些刻意营造的案例中,启用某些转换有可能会打断代码的逻辑,但绝大部分情况下是安全的。你可能会想尝试一下,因为这毕竟会减少文件体积。以下是某些例子:

  • new Array(1, 2, 3)Array(1, 2, 3)[ 1, 2, 3 ]
  • new Object(){}
  • String(exp)exp.toString()"" + exp
  • new Object/RegExp/Function/Error/Array (...) → 我们干掉用new
  • void 0undefined (假如作用域中有一个变量名叫”undefined”;我们这么做是因为变量名会被混淆成单字符)

编译条件语句

Conditional compilation

Uglify会假设全局变量都是常量(不管是否在局部域中定义了),你可以用--define (-d)来实现定义全局变量。例如你传--define DEBUG=false,UglifyJS会在输出中干掉下面代码:

  1. if (DEBUG) {
  2. console.log("debug stuff");
  3. }

你可以像--define env.DEBUG=false这样写嵌套的常量。

在干掉那些永否的条件语句以及不可达代码时,UglifyJS会给出警告。现在没有选项可以禁用此特性,但你可以设置 warnings=false 来禁掉所有警告。

另一个定义全局常量的方法是,在一个独立的文档中定义,再引入到构建中。例如你有一个这样的build/defines.js

  1. const DEBUG = false;
  2. const PRODUCTION = true;
  3. // 等等

这样构建你的代码:

  1. uglifyjs build/defines.js js/foo.js js/bar.js... -c

UglifyJS会注意到这些常量。因为它们无法改变,所以它们会被认为是没被引用而被照样干掉。如果你用const声明,构建后还会被保留。如果你的运行环境低于ES6、不支持const,请用var声明加上reduce_vars设置(默认启用)来实现。

编译条件语句API

你也可以通过程序API来设置编译配置。其中有差别的是一个压缩器属性global_defs

  1. var result = UglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
  2. compress: {
  3. dead_code: true,
  4. global_defs: {
  5. DEBUG: false
  6. }
  7. }
  8. });

global_defs"@"前缀的表达式,UglifyJS才会替换成语句表达式:

  1. UglifyJS.minify("alert('hello');", {
  2. compress: {
  3. global_defs: {
  4. "@alert": "console.log"
  5. }
  6. }
  7. }).code;
  8. // returns: 'console.log("hello");'

否则会替换成字符串:

  1. UglifyJS.minify("alert('hello');", {
  2. compress: {
  3. global_defs: {
  4. "alert": "console.log"
  5. }
  6. }
  7. }).code;
  8. // returns: '"console.log"("hello");'

使用minify()获得原生UglifyJS ast

Using native Uglify AST with minify()

  1. // 例子: 只解析代码,获得原生Uglify AST
  2. var result = UglifyJS.minify(code, {
  3. parse: {},
  4. compress: false,
  5. mangle: false,
  6. output: {
  7. ast: true,
  8. code: false // optional - faster if false
  9. }
  10. });
  11. // result.ast 即是原生 Uglify AST
  1. // 例子: 输入原生 Uglify AST,接着把它压缩并混淆,生成代码和原生ast
  2. var result = UglifyJS.minify(ast, {
  3. compress: {},
  4. mangle: {},
  5. output: {
  6. ast: true,
  7. code: true // 可选,false更快
  8. }
  9. });
  10. // result.ast 是原生 Uglify AST
  11. // result.code 是字符串格式的最小化后的代码

使用 Uglify AST

Working with Uglify AST

可以通过TreeWalkerTreeTransformer分别横截(?transversal)和转换原生AST。

ESTree/SpiderMonkey AST

UglifyJS有自己的抽象语法树格式;为了某些现实的原因
我们无法在内部轻易地改成使用SpiderMonkey AST。但UglifyJS现在有了一个可以输入SpiderMonkeyAST的转换器。
例如Acorn ,这是一个超级快的生成SpiderMonkey AST的解释器。它带有一个实用的迷你CLI,能解释一个文件、把AST转存为JSON并标准输出。可以这样用UglifyJS来压缩混淆:

  1. acorn file.js | uglifyjs --spidermonkey -m -c

-p --spidermonkey选项能让UglifyJS知道输入文件并非JavaScript,而是SpiderMonkey AST生成的JSON代码。这事我们不用自己的解释器,只把AST转成我们内部AST。

使用 Acorn 来解释代码

Use Acorn for parsing

更有趣的是,我们加了 -p --acorn选项来使用Acorn解释所有代码。如果你传入这个选项,UglifyJS会require("acorn")

Acorn确实非常快(650k代码原来要380ms,现在只需250ms),但转换Acorn产生的SpiderMonkey树会额外花费150ms。所以总共比UglifyJS自己的解释器还要多花一点时间。

Uglify快速最小化模式

Uglify Fast Minify Mode

很少人知道,对大多数js代码而言,其实移除空格和混淆符号已经占了减少代码体积之中到的95%—不必细致地转换。简单地禁用压缩compress能加快UglifyJS的构建速度三四倍。我们可以比较一下
butternut和只使用混淆mangle的模式的Uglify的压缩速度与gzip大小:
butternut:

d3.js minify size gzip size minify time (seconds)
original 451,131 108,733 -
uglify-js@3.0.24 mangle=false, compress=false 316,600 85,245 0.70
uglify-js@3.0.24 mangle=true, compress=false 220,216 72,730 1.13
butternut@0.4.6 217,568 72,738 1.41
uglify-js@3.0.24 mangle=true, compress=true 212,511 71,560 3.36
babili@0.1.4 210,713 72,140 12.64

在CLI中,这样启用快速最小化模式:

  1. uglifyjs file.js -m

API这样用:

  1. UglifyJS.minify(code, { compress: false, mangle: true });