许超前的博客 - A longker in the Earth

November 17, 2009

试用Google Closure Compiler的API

Filed under: Daily Life — 许超前 @ 7:22 pm

原来的JS压缩服务采用的是YUI Compressor,一年下来表现还算稳定。

今天老高说想尝试一下Google Closure Compiler,翻了翻文档,看看有没API能直接用的:传一个原始字符串,返回一个压缩后的字符串。找了半天,没有此类的例子(看来设计者认为像我们这样直接采用API而不是使用命令行的用户不多)。

查看JavaDoc,定位到com.google.javascript.jscomp.Compiler,有如下描述:
Compiler (and the other classes in this package) does the following:
* parses JS code
* checks for undefined variables
* performs optimizations such as constant folding and constants inlining
* renames variables (to short names)
* outputs compact javascript code
看来就是这个了。

相中方法: Result compile(JSSourceFile extern, JSSourceFile input, CompilerOptions options) 。
input和options容易理解,extern是什么?其实类的描述里也稍微提了下:
External variables are declared in ‘externs’ files. For instance, the file may include definitions for global javascript/browser objects such as window, document.
很显然可以没有extern(但不能为NULL,设置方法参看底下的代码)。

按道理,编译后返回的result里应该就有我想要的结果了吧,结果出乎意料。Result只有编译状态的描述。难道此路不通?

又从JAR包的META-INF看到入口类是CompilerRunner,一路追踪下去。。。中间省略文字几千字。绕了半天,得出的结论是:和CompilerRunner相关的几个类很难复用,想用的方法要么是不可见,要么是包可见,要么是子类可见。代理来继承去,眼看就OK了,又发现很多状态居然是static的,这使得程序的状态是迭加的。看来此路也不通。。。又绕回Compiler。晕,原来Compiler有一个方法toSource()就是返回压缩化的代码的。

这下好办了。主要代码如下:
final ByteArrayOutputStream err = new ByteArrayOutputStream();
final PrintStream errWrapper = new PrintStream(err);
final Compiler compiler = new Compiler(errWrapper);

final ByteArrayInputStream bais = new ByteArrayInputStream(codeBytes);

final CompilerOptions options = new CompilerOptions();
final CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS
level.setOptionsForCompilationLevel(options);

final Result status = compiler.compile(JSSourceFile.fromCode("extern", ""), JSSourceFile.fromInputStream("input", bais), options);
System.out.println(compiler.toSource);

把代码布上后,测试通过。本来这是一件小事,没必要以此作为BLOG的内容。但是这件事也是给了我几个感触,觉得有必要说一说:
一、文档很重要。简单的几句话,也许就能让用户少走很多弯路。DAL后面一定要多写文档,特别是使用手册。
二、API设计要符合直觉。不符合直觉的设计会造成大量的困扰。当年Lucene的delete操作,用脚跟想想就应该放在IndexWriter里,可设计者却把它放在IndexReader里,结果邮件列表里经常出现此类的问题。当然,后续版本已经做了修改(IndexWriter有了delete方法)。用户不会关心你为什么这么做;新用户要抛弃你只要点一下鼠标就可以了;而老用户选择离开只需要皱一下眉头。
三、时间管理很重要。任何事情都应该在任务框架里进行;否则,很多东西可能会失控。

—The end.

3 Comments »

  1. 我也以为很简单,没想到又失控了。。。:(
    其实一般来说,任务的时间管理总是会失控的,总会有各种各样你想不到的问题出现。我觉得只有两种对应方法,1:如果任务不重要,就先挂起,估计已知情况重新编排时间;2:如果任务重要,就先做,再重新编排时间;但是时候都要从中寻找为什么没有准确掌控时间,到底是哪里的问题,人的,需求的,还是什么的。
    不管找不找得到,起码我们把问题想少了。

    Comment by Paul Gao — November 18, 2009 @ 11:45 am

  2. 突然想起来一个问题,其实最麻烦的问题是在你干了一半发现任务没那么简单,能解决,但又比之前要投入更多时间的时候,怎么选择下一步的行为?
    我觉得大部分情况下,大家选择的都是迎难而上,继续进行(包括你,呵呵),因为停止等后面再说的话,可能花费的总体时间可能会更多,因为你又要再一次的重新适应任务。
    其实我下午几次问你的时候,潜台词意思就是麻烦的话就先算了,反正目前的服务跑的还不错。呵呵。

    Comment by Paul Gao — November 18, 2009 @ 11:53 am

  3. 交换博客链接了

    Comment by simaliu — November 18, 2009 @ 10:56 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress, 京ICP备09047672号