Uglify-js不会破坏变量名称

时间:2012-06-09 07:13:06

标签: javascript performance compression minify uglifyjs

尝试为我的js库准备好构建环境。根据网络上的评论UglifyJS似乎是最好的压缩模块之一,在NodeJS下工作。所以这里是推荐最小化代码的方法:

var jsp = require("uglify-js").parser;
var pro = require("uglify-js").uglify;

var orig_code = "... JS code here";
var ast = jsp.parse(orig_code); // parse code and get the initial AST
ast = pro.ast_mangle(ast); // get a new AST with mangled names
ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
var final_code = pro.gen_code(ast); // compressed code here

如此处所示,pro.ast_mangle(ast)应该破坏变量名称,但事实并非如此。我从这个管道中得到的只是javascript代码,没有空格。起初我认为我的代码没有针对压缩进行优化,但后来我用Google Closure进行了尝试并得到了相当大的压缩(带有错误的变量名称和所有内容)。

UglifyJS专家,任何暗示我做错了什么?

更新

实际代码太大而无法在此处引用,但即使是这样的代码段也不会被破坏:

;(function(window, document, undefined) {

    function o(id) {
        if (typeof id !== 'string') {
            return id;  
        }
        return document.getElementById(id);
    }   

    // ...

    /** @namespace */
    window.mOxie = o;

}(window, document));

这就是我得到的(我想只有空格被剥离):

(function(window,document,undefined){function o(id){return typeof id!="string"?id:document.getElementById(id)}window.mOxie=window.o=o})(window,document)

4 个答案:

答案 0 :(得分:15)

好吧,似乎最新版本的Uglify JS要求将mangle选项显式传递为true,否则它不会破坏任何东西。像这样:

var jsp = require("uglify-js").parser;
var pro = require("uglify-js").uglify;

var orig_code = "... JS code here";
var options = {
    mangle: true
};

var ast = jsp.parse(orig_code); // parse code and get the initial AST
ast = pro.ast_mangle(ast, options); // get a new AST with mangled names
ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
var final_code = pro.gen_code(ast); // compressed code here

答案 1 :(得分:10)

默认情况下,uglify不会破坏顶级名称,也许这就是你所看到的?

尝试: -mt或--mangle-toplevel - 顶层范围内的mangle名称(默认情况下我们不这样做)。

答案 2 :(得分:1)

如果您使用的是Uglify2,则可以使用TopLevel.figure_out_scope()http://lisperator.net/uglifyjs/scope

如果你使用的是Uglify1,那就有点复杂了。这是我通过修改Uglify's squeeze_more.js file的代码放在一起的一些代码:

function eachGlobalFunctionCall(ast, callback) {
  var w = uglify.uglify.ast_walker(),
      walk = w.walk,
      MAP = uglify.uglify.MAP,
      scope;

  function with_scope(s, cont) {
    var save = scope, ret;
    scope = s;
    ret = cont();
    scope = save;
    return ret;
  }

  function _lambda(name, args, body) {
    return [ this[0], name, args, with_scope(body.scope, curry(MAP, body, walk)) ];
  }

  w.with_walkers({
    "function": _lambda,
    "defun": _lambda,
    "toplevel": function(body) {
      return [ this[0], with_scope(this.scope, curry(MAP, body, walk)) ];
    },
    "call": function(expr, args) {
      var fnName = expr[1];

      if (!scope.has(fnName)) {    // <--- here's the important part
        callback(fnName, args, scope);
      }
    }
  }, function() {
    return walk(uglify.uglify.ast_add_scope(ast));
  });
}

上面的这个只适用于全局函数调用,但它会为你提供一个回调函数,当walker找到对未知(全局)方法的调用时执行该回调。

例如,给出以下输入:

function foo () {
  bar(1);
  (function () {
    function bar() { }
    bar(2);
    (function () {
      bar(3);
    }());
  }());
}

它会找到bar(1)来电,但不是 bar(2)bar(3)

答案 3 :(得分:0)

全局范围内的变量可用于任何其他脚本,因此如果没有特殊的开关,Uglify将不会更改它们,以防您确实需要它们可见。您可以使用-mt / toplevel切换/设置,或者更好地停止污染全局范围,并清楚地表明您不打算在外部看到这些变量,而是构建代码框架进入匿名自调用函数,作为私有范围。