通过定义常量控制Closure Compiler的行为
2011-04-11 01:12 by 老赵, 2940 visits上一篇文章里我提到,在进行Closure Compiler压缩之前可以对代码进行一些预处理,这样可以得到更好的效果。在回复中有朋友提到可以使用一些Annotation(标记),例如加上@export,然后使用--generate_exports,便可以保留需要的那些变量名。不过经过实验还是没有得到预期的效果,所以使用标记来“指导”高级压缩行为依旧是一个不太可行的做法。不过有个标记与我的设想一直,那就是使用@define来“定义一个常量”,然后在编译(压缩)时对其进行覆盖。这为一些压缩需求提供一种更直接的控制方式。
还是先谈一下@export,当时看了文档的说明,我一度也觉得这能满足我的要求(即保留变量名不被压缩):
$ java -jar compiler.jar --help ... --generate_exports : Generates export code for those marked with @export ...
但事实上,实验的结果令人大失所望。例如下面的代码:
/** * @export */ var Jscex = (function () { /** * @constructor */ var CodeGenerator = function () { /** * @export */ this.normalMode = false; } /** * @export */ CodeGenerator.prototype.generate = function () { alert(this.normalMode); } function compile() { return new CodeGenerator(); }; return { compile: compile }; })();
首先,Closure Compiler只允许在Global范围内使用@export标记,因此以上几个@export只有Jscex上的那个才能保留下来。其次,当去除其他@export标记之后,Closure Compiler生成的结果却是:
var b=function(){...};goog.a("Jscex",b);
Closure Compiler很神奇地为脚本添加了Closure Libraries这个依赖,这显然无法令人接受。
失望之余,我观察并尝试了其他一些标记,发现@define还是不错的。就拿前文来说,我希望Jscex能够在Debug模式下避免生成不必要的括号,原来的做法是编写这样的代码:
"dot": function (ast) { function needBracket() { /* ... */ } var nb = needBracket(); if (nb) { this._write("(") ._visit(ast[1]) ._write(").") ._write(ast[2]); } else { this._visit(ast[1]) ._write(".") ._write(ast[2]); } },
然后在使用Closure Compiler压缩之前将needBracket方法的调用替换成true,这样Closure Compiler就会发现if的第二个分支永远不会执行,因此直接从最后的结果中去除了;更近一步,由于needBracket方法没有其他地方调用过,因此它也会被完整地删除,这就得到了体积更小的代码。不过这个方法多少有点trick的意味,它需要代码编写者与压缩脚本之间进行约定,其实我们可以使用一种更直接的方式,那就是使用@define标记来定义一个常量:
/** @define {boolean} */ var JSCEX_DEBUG = true; ... "dot": function (ast) { function needBracket() { /* ... */ } var nb = (!JSCEX_DEBUG) || needBracket(); if (nb) { this._write("(") ._visit(ast[1]) ._write(").") ._write(ast[2]); } else { this._visit(ast[1]) ._write(".") ._write(ast[2]); } },
在代码中JSCEX_DEBU为true,因此nb的值会是needBracket方法调用后的结果。但如果我们在使用Closure Compiler压缩代码添加--define参数,便可以将JSCEX_DEBUG的值修改为false:
java \ -jar compiler.jar \ --js jscex.js \ --js_output_file jscex.min.js \ --define 'JSCEX_DEBUG=false' \ --compilation_level ADVANCED_OPTIMIZATIONS
于是乎——就不多做解释了。Closure Compiler提供的标记有很多,但实验下来行为大都和想象中不符,这倒也是件很奇怪的事情。可能还是要去邮件列表之类的地方问问吧。
广告时间:第四届nBazaar技术交流会将于2011年4月23日举行。根据部分用户反馈,第四届交流会的形式将略作改变:除了三场演讲之外,本次活动设有嘉宾互动环节,您将有机会和嘉宾就某些话题进行探讨。我们将在会前收集部分话题,也希望大家踊跃提问,具体方式详见 http://nbazaar.org/。
居然过了那么久还可以抢到沙发啊。