Hello World
Spiga

在项目中使用Google Closure Compiler

2009-12-09 09:13 by 老赵, 8185 visits

现在的Web项目总是离不开大量JavaScript,而JS文件的体积也越来越大,也越来越影响页面的感知性能(Perceived Performance)。因此,我们会对JS文件进行压缩,一方面是使用Gzip,而另一方面则是去除JS文件里的注释、空白,并且压缩局部变量长度等等。对于一些成熟的类库来说,它们本身都会提供“完整注释”以及“强烈压缩”两个版本。但是,有时候我们需要自己修复类库里的bug,这只能在注释版中修改,对于压缩版自然就无能为力了。此外,自定义的脚本文件一般也值得一压。因此我在项目中时常会备一个脚本压缩工具。

压缩脚本的工具有很多,例如老牌的JSMin,或是YUI Compressor(下称YC),它们都可以用来压缩脚本文件(后者还可以处理CSS)。不过在新项目中,我使用了新的工具:Google Closure Compiler(下称GC)。GC有多种用法,例如网页版网络API版,还有独立应用程序版。GC与YC不同的是,YC是一个压缩器(Compressor),而GC更是一个编译器(Compiler),也就是说GC的压缩并不仅仅是去除注释和空白,还可以在保证代码正确性的情况下进一步地改写成更省空间的做法,一个字节算一个字节,例如:

a = new Object    => a = {}
a = new Array     => a = []
if (a) b()        => a && b()
return 2 * 3;     => return 6;

GC还提供了一些更危险的压缩方式,虽然有神奇效果,但个人不建议使用。关于YC和GC更详细的对比及注意事项,可以参考淘宝UED部门lifesinger所制作的无比精彩的幻灯片:

GC使用Java编写(YC也一样,不过它有.NET移植版),它的独立应用程序版是一个jar包。如果您不想装一个Java Runtime的话,则可以使用它的网络API版。但是,我在项目中却使用了另一种方式:即不需要安装JRE,也不需要依赖于网络。这个方式便是借助IKVM.NET将GC转化为.NET使用——还记得上次的Lucene 2.9吗

我的项目组织结构大致是:

\src\Web.UI\Scripts\                   <- 存放JS脚本的目录
\tools\IKVM.NET\                       <- 存放IKVM.NET相关文件
\tools\closure-compiler\compiler.jar   <- GC的jar包
\tools\closure-compiler\build.bat      <- 将jar转化为exe的脚本
\tools\closure-compiler\compress.ps    <- 压缩JS的PowerShell命令

以上便是所有放入SVN中的文件,每个开发人员在使用GC之前,首先需要调用build.bat进行“重新编译”:

..\ikvm.net\ikvmc.exe compiler.jar -out:compiler.exe -target:exe
xcopy ..\ikvm.net\*.dll . /y /q

这两行脚本会将compiler.jar包编译为compiler.exe文件,并将IKVM.NET中需要的文件复制到closure-compiler目录下。于是,借助PowerShell的管道,便可以压缩Scripts目录下所有的JS文件:

dir -path ..\..\src\Web.UI\Scripts -filter *.js | % { .\compiler.exe --js $_.FullName --js_output_file ($_.FullName -replace ".js", ".min.js") }

这样,xxx.js便会被压缩为xxx.min.js。于是再配合项目中的扩展方法:

public static string Script(this HtmlHelper helper, string path)
{
    return "<script language=\"javascript\" type=\"text/javascript\" src=\"" + path +
        (helper.ViewContext.HttpContext.IsDebuggingEnabled ? ".min.js" : ".js") +
        "\"></script>";
}

万事俱备。

有些朋友时不时会羡慕其他平台上项目丰富,而在.NET平台上做一些事情感觉捉襟见肘的。我以前经常说,又何必把平台划分的那么细,大家既然都在为技术社区作贡献,那么思想或是做法都是可以借鉴的,所以也已经有了那么多移植过来的项目。而现在,可以“借鉴”的已经不只是“思想”,而是真正实际的项目!我相信在不久的将来,随着IronPython和IronRuby等项目愈发成熟,可以在.NET上运行的东西会越来越多(事实上,如IronPython其实已经很成熟了)。

嗯,到时候,Python的就是.NET的,Ruby的也是.NET的,而Java的——它还是.NET的。

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

43 条回复

  1. 小鬼00[未注册用户]
    *.*.*.*
    链接

    小鬼00[未注册用户] 2009-12-09 09:23:00

    哈,又认识多了个GC了.

  2. Gnie
    *.*.*.*
    链接

    Gnie 2009-12-09 09:24:00

    沙发慢慢看

  3. killkill
    *.*.*.*
    链接

    killkill 2009-12-09 09:25:00

    顶,平台之见才是让人“捉襟见肘”的真正原因。

  4. JimLiu
    *.*.*.*
    链接

    JimLiu 2009-12-09 09:26:00

    沙发?
    方案1:不要用
    方案2:参考方案1

    外国人也玩这套?

  5. 老赵
    admin
    链接

    老赵 2009-12-09 09:33:00

    @JimLiu
    1、外国人当然玩
    2、这人是中国人

  6. Will Meng
    *.*.*.*
    链接

    Will Meng 2009-12-09 09:44:00

    GC?老赵还是叫GCC吧,容易让人想起垃圾回收来。
    另外,那个扩展方法很有意思,比之前我在aspx页面的做法要好。

  7. 老赵
    admin
    链接

    老赵 2009-12-09 09:53:00

    @Will Meng
    GCC也有别的意思,为了和幻灯片保持一致,就用GC了。

  8. keyapril
    *.*.*.*
    链接

    keyapril 2009-12-09 10:00:00

    不知道老赵有没有看过Microsoft Ajax Minifier工具

  9. Jake Lin
    *.*.*.*
    链接

    Jake Lin 2009-12-09 10:08:00

    这个不错。

  10. 老赵
    admin
    链接

    老赵 2009-12-09 10:12:00

    @keyapril
    听说过,没用过,比GC好吗?

  11. keyapril
    *.*.*.*
    链接

    keyapril 2009-12-09 10:17:00

    @Jeffrey Zhao
    GC有的选择项应该都有,很好用的一点是可以集成到VS中。
    推荐安装一用。

  12. 假正经哥哥
    *.*.*.*
    链接

    假正经哥哥 2009-12-09 10:27:00

    Microsoft Ajax Minifier 有三种方式可以用
    1:命令行
    2:集成到项目编译时
    3:提供类库在运行时

    另:是否把.net 版的GCC 提供下载一下?

    我用parker.net ...比较多。。

  13. airy
    *.*.*.*
    链接

    airy 2009-12-09 10:36:00

    到时候,Python的就是.NET的,Ruby的也是.NET的,而Java的——它还是.NET的。

  14. 怪客
    *.*.*.*
    链接

    怪客 2009-12-09 10:39:00

    一直用 Google Closure Compiler 压缩!

  15. 老赵
    admin
    链接

    老赵 2009-12-09 10:41:00

    @假正经哥哥
    我的文章其实就是命令行,既然命令行能跑自然也能做类库用,至于集成到编译时,自己写Post build就行。
    为什么总要我提供下载呢?我的文章已经写这么详细,一步一步做出来又有何难。

  16. Avlee
    *.*.*.*
    链接

    Avlee 2009-12-09 10:44:00

    其实各有所长

    YUI Compressor相对要安全一些,中规中举。

    Microsoft Ajax Minifier,我使用的原因是:
    1、.Net实现。
    2、它提供将JS代码中的中文转为ACSII形式,当然也可通过java的命令行工具处理,不过就显得罗嗦了。
    3、不像YUIC只提出优化建议,而是执行一些优化,特别是变量定义。

    GC,这个东西就相对强大的多了,处理中文和Ajaxmin一样也不错,更夸张的是他会根据自己的规则重新编译你的代码,这些高级应用要掌握了才干在实际中使用,不过确实是个优秀的工具,也是个危险的工具。

  17. Avlee
    *.*.*.*
    链接

    Avlee 2009-12-09 10:50:00

    当然GC还有个强大的模块化编译的功能,特别是在处理分布在不公脚本文件的模块化脚本代码时非常有用,现在我只是通过编写另外的工具来合并脚本文件。

  18. Ariex
    *.*.*.*
    链接

    Ariex 2009-12-09 10:51:00

    看到GC对eval的支持不是很好,不过可以通过对eval中的内容进行二次解析解决,估计下一版这个就不是问题了

  19. airy
    *.*.*.*
    链接

    airy 2009-12-09 11:00:00

    下面这句来自Google:
    The Closure Compiler has also been integrated with Page Speed, which makes it easier to evaluate the performance gains you can get by using the compiler.

    问题:
    是不是基于FireBug的Page Speed插件的Minify JavaScript的功能生成的压缩版的JS文件就是和你指的这个功能一样呢?

  20. 假正经哥哥
    *.*.*.*
    链接

    假正经哥哥 2009-12-09 11:14:00

    Jeffrey Zhao:
    @假正经哥哥
    我的文章其实就是命令行,既然命令行能跑自然也能做类库用,至于集成到编译时,自己写Post build就行。
    为什么总要我提供下载呢?我的文章已经写这么详细,一步一步做出来又有何难。


    因为对于这个只要结果就够了,过程对我来说并非重要。。

  21. liyou
    *.*.*.*
    链接

    liyou 2009-12-09 11:34:00

    能请教一个问题吗?

    一张网页有一张图片<img src="abc.jpg"/>。
    当用户请求这张网页时,根据不同的用户,服务器返回的图片是不同的(是其他路径下的图片),但客户端看到的图片的src还是"abc.jpg"。
    请问要怎样实现这种功能呢?

    这是我在别的地方的提问http://topic.csdn.net/u/20091127/20/033f9ef2-b7ef-4a46-a5b3-db3bfe6a3485.html

  22. 沙加
    *.*.*.*
    链接

    沙加 2009-12-09 11:43:00

    @假正经哥哥
    您也不给老赵发工钱,人家也不能免费给您劳动,是不?

  23. 徐少侠
    *.*.*.*
    链接

    徐少侠 2009-12-09 11:44:00

    @liyou
    服务器使用过滤
    用自定义类返回选择后的图片
    参考以下园子里的文章
    什么是.ashx文件

  24. 徐少侠
    *.*.*.*
    链接

    徐少侠 2009-12-09 11:46:00

    @liyou
    上面那个不能形成abc.jpg
    用这个
    HttpHandler

  25. 老赵
    admin
    链接

    老赵 2009-12-09 11:56:00

    假正经哥哥:
    因为对于这个只要结果就够了,过程对我来说并非重要。。


    作为开发人员不能这么偷懒的……

  26. 老赵
    admin
    链接

    老赵 2009-12-09 11:56:00

    Ariex:看到GC对eval的支持不是很好,不过可以通过对eval中的内容进行二次解析解决,估计下一版这个就不是问题了


    eval is evil,项目中可以避免的时候还是不要使用eval了,呵呵

  27. 老赵
    admin
    链接

    老赵 2009-12-09 11:58:00

    airy:
    问题:
    是不是基于FireBug的Page Speed插件的Minify JavaScript的功能生成的压缩版的JS文件就是和你指的这个功能一样呢?


    我不知道。

  28. liyou
    *.*.*.*
    链接

    liyou 2009-12-09 11:59:00

    IHttpHandler

    public void ProcessRequest(HttpContext context)
    我也是这样做的啊,在我本机上可以,可放到服务器上,检测发现,不会进入到ProcessRequest这个方法里,郁闷了很久。。。

  29. 老赵
    admin
    链接

    老赵 2009-12-09 11:59:00

    Avlee:
    GC,这个东西就相对强大的多了,处理中文和Ajaxmin一样也不错,更夸张的是他会根据自己的规则重新编译你的代码,这些高级应用要掌握了才干在实际中使用,不过确实是个优秀的工具,也是个危险的工具。


    GC有三种级别,Simple Optimization是安全的吧,危险的是Advanced Optimization,我文章里写了不建议使用。

  30. 1-2-3
    *.*.*.*
    链接

    1-2-3 2009-12-09 12:32:00

    斯密达

  31. RednaxelaFX
    *.*.*.*
    链接

    RednaxelaFX 2009-12-09 13:49:00

    那个投影片的第18页介绍的技巧很大程度上就是让我们人肉做公共子表达式消除(CSE,common sub-expression elimination)……当然,做了不是坏事。很多时候给公共子表达式一个名字对代码的可阅读性是有帮助的。
    不过有些优化做得还不够的JS引擎在处理字面量跟变量时会生成不同的代码……字面量的类型很明确,方便优化;变量所指向的值在运行时的类型是什么就不一定,需要尝试做推导来看能否静态确定。早前版本的V8对下面两种形式生成的代码就会不同:

    function foo() {
      var x = 1
      return x + 2
    }

    function foo() {
      return 1 + 2
    }

    于是……我们还是得指望JS引擎们多努力了 T T

  32. 辰
    *.*.*.*
    链接

    2009-12-09 15:18:00

    google 似乎在提速方面下了很大的努力。

    如果按照相同的思路,对c#代码进行压缩提速,应该挺好玩,

  33. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-12-09 16:21:00

    辰:
    google 似乎在提速方面下了很大的努力。

    如果按照相同的思路,对c#代码进行压缩提速,应该挺好玩,




    C#代码是编译执行的,编译成IL本身就是一个压缩的过程。

  34. JimLiu
    *.*.*.*
    链接

    JimLiu 2009-12-09 16:38:00

    @Ivony...
    编译成IL的时候似乎压缩的比较少,难道是为了反编译而生?真正大量的优化好像是在JIT上的。

  35. 老赵
    admin
    链接

    老赵 2009-12-09 16:42:00

    @JimLiu
    这不一定是压缩吧,也可以是分解(语法糖等等)。
    感觉C#这种还是别往“压缩”上靠了……

  36. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-12-09 17:18:00

    JimLiu:
    @Ivony...
    编译成IL的时候似乎压缩的比较少,难道是为了反编译而生?真正大量的优化好像是在JIT上的。




    是反射不是反编译。

  37. 麦子|君子兰
    *.*.*.*
    链接

    麦子|君子兰 2009-12-10 20:41:00

    老赵就是个宝藏,每天都能挖点东西走 嘿嘿

  38. Kevin Dai
    *.*.*.*
    链接

    Kevin Dai 2009-12-11 11:42:00

    哇塞 JavaScript的压缩工具涉及到的技术已经用上编译技术了 除了之前的一半压缩方法和现在google提倡的方法 应该没有更高级别的压缩方法了吧

  39. annliu
    *.*.*.*
    链接

    annliu 2009-12-16 16:56:00

    怎么不能压缩子文件夹里的js文件呢?

    @echo off
    for /r d:\compiler %%i in (*.js) do (

    echo compiling %%i

    java -jar compiler.jar --js %%i --js_output_file %%i_compiler

    echo compiled %%i
    )
    pause

  40. 老赵
    admin
    链接

    老赵 2009-12-16 19:27:00

    @annliu
    不了解bat鸟……

  41. cloudgamer
    *.*.*.*
    链接

    cloudgamer 2010-01-20 10:25:00

    貌似网页版,网络API版都打不开呢

  42. zicjin
    222.66.81.*
    链接

    zicjin 2011-06-23 15:31:45

    请教powershell里 % {} 这个语法具体代表什么意思? 我搜了整本《powershell-userguide》也没找到相关解释

  43. hzsong123
    221.219.197.*
    链接

    hzsong123 2014-12-15 22:53:50

    老赵,我最近准备将若干个Java项目转化为C#项目(源码级转化),我选择了Sharpen+IKVM.net,但是IKVM不支持泛型,很是令人头疼,你有什么好的建议吗? 如果只依靠IKVM.net,只能得到dll,并不能得到源码(反编译没有源码注释信息),我也是很恼火。 谢谢你的帮助。黄志松,hzsong123@126.com

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我