zoukankan      html  css  js  c++  java
  • 使用Google Closure Compiler高级压缩Javascript代码

    背景

    前端开发中,特别是移动端,Javascript代码压缩已经成为上线必备条件。

    如今主流的Js代码压缩工具主要有:

    1)Uglify http://lisperator.net/uglifyjs/
    2)YUI Compressor http://developer.yahoo.com/yui/compressor/
    3)Google Closure Compiler https://developers.google.com/closure/compiler/

    自从jQuery等流行库使用Uglify作为压缩工具,Uglify慢慢流行起来,如今也是很多开发工具(框架)默认使用的Js压缩工具,比如百度的Fis以及绝大部分的Yeoman脚手架等。YUI Compressor也逐渐被Uglify所替代。

    Uglify的压缩策略较为安全,所以不会对源代码进行大幅度的改造,压缩相对较为保守。所以将通过Uglify压缩后的代码格式化之后,还是大致能看明白。

    而Google Closure Compiler(以下简称GCC)的高级压缩则是完全重新改造JS代码,去除代码中一些可以直接输出运行结果的逻辑,去除根根本不会执行到的JS代码,从而压缩效率会更高。

    GCC三种压缩模式

    GCC提供三种压缩模式:

    1)Whitespace only
    2)Simple
    3)Advanced
    

    我们以这段简单的代码为例

    function sayHello(name) {
      alert('Hello, ' + name);
    }
    sayHello('binnng');
    

    分别使用这三种压缩模式进行压缩:

    Whitespace only

    function sayHello(name){alert("Hello, "+name)}sayHello("binnng");
    

    发现只是简单的去除空格换行注释。

    Simple

    function sayHello(a){alert("Hello, "+a)}sayHello("binnng");
    

    Whitespace only要高端一点,在其基础上,还对局部变量的变量名进行缩短。这也是其他压缩工具所使用的压缩方式,如Uglify等,也是最为主流的压缩方式。比较安全。

    Advanced

    alert("Hello, binnng");
    

    会发现,Advanced级别的压缩改变(破坏)了原有代码结构,直接输出了代码最终运行结果,可见的确是分析重写破坏,但是对代码压缩做到了极致,极大的减少了代码量。

    注意的地方

    正因为GCC是这样的破坏性压缩工具,所以使用起来得异常小心,稍微不规范可能就会引起压缩报错或者压缩成功后却不能正常运行。那么,如果要使用Advanced级别压缩,要注意哪些呢?

    以下所有未指名级别的GCC压缩均为Advanced级别。

    GCC会对变量的属性名也进行压缩

    var data = {
      user: "binnng",
      age: "18"
    };
    
    if ("user" in data) {
      alert(data.age);
    }
    

    经过Uglify压缩:

    var data={user:"binnng",age:"18"};"user"in data&&alert(data.age);
    

    经过GCC压缩:

    var a={b:"binnng",a:"18"};"user"in a&&alert(a.a);
    

    会发现GCC压缩时,将变量的属性名也缩短,代码量减少了,但是却带来了问题,会发现以上GCC压缩后的代码运行其实是不正确的。因为属性名缩短改变后,data已经不再拥有名为user的属性了。

    那如何解决呢?

    var data = {
      user: "binnng",
      age: "18"
    };
    
    if (data.user) {
      alert(data.age);
    }
    

    这时候再经过GCC压缩:

    alert("18");
    

    直接输出了正确的结果。

    如果不想让GCC压缩属性名,比如在Ajax请求发送给后端接口的时候,可以将属性名用双引号包裹:

    var data = {
      "user": "binnng",
      "age": "18"
    };
    
    var ajax = function(url, data, callback) {
      (new window.XMLHttpRequest()).send(data);
      // ...
    }; 
    
    ajax("//baidu.com", data, function(res) {console.log(res)});
    

    这样经过GCC压缩后:

    (new window.XMLHttpRequest).send({user:"binnng",age:"18"});
    

    原封不动的保留了后端需要的参数名。

    全局变量要显式挂载在window下

    var foo = "1234";
    alert(widnow.foo);
    

    这时候,经过GCC压缩后:

    alert(window.a);
    

    有点莫名其妙。。因为在GCC中,不再认可隐式全局变量,所以上面的代码中在GCC眼里,foo是没有挂载到window下的,所以下文的window.foo其实是未定义的。

    要解决这个问题,必须将foo显式挂载到window下:

    window.foo = "1234";
    alert(widnow.foo);
    

    这样经过压缩后:

    window.a="1234";alert(widnow.a);
    

    这时候可能就会疑问,为何不直接压缩成alert("1234")呢?这是因为GCC不会去除挂载在window下的变量

    必要时导出变量函数等

    window.btnClick = function() {
      alert("clicked");
    };
    

    以上的代码经过压缩后成为:

    window.a=function(){alert("clicked")};
    

    此时,如果HTML中存在如下代码,就会报错。

    <a onclick="btnClick()">点我</a>
    

    这时候就需要导出函数

    window["btnClick"] = function() {
      alert("clicked");
    };
    

    用双引号包裹后,btnClick就保留了下来。在构造函数中,尤其需要注意导出,例如:

    var Animal = function(name) {
      this.name = name;
    };
    Animal.prototype.eat = function() {
      alert(this.name + " is eating!");
    };
    

    以上的代码经过GCC压缩后,什么都没剩下,因为GCC认为,代码中的代码都没有执行,属于dead code。既然这么写了,那肯定是需要它,提供给别人外部调用什么的,这时候就需要这么导出:

    var Animal = function(name) {
      this.name = name;
    };
    Animal.prototype.eat = function() {
      alert(this.name + " is eating!");
    };
    
    window["Animal"] = Animal;
    Animal.prototype["eat"]= Animal.prototype.eat;
    

    经过压缩后:

    function a(b){this.name=b}a.prototype.a=function(){alert(this.name+" is eating!")};window.Animal=a;a.prototype.eat=a.prototype.a;
    

    这时候,Animal这个方法成功的保留了下来。

    还有,在使用JSONP方式获取服务端数据的时候,也一定要导出callback方法名:

    var jsonpCb = function() {
      //...
    };
    
    getScript("/api/data?callback=jsonpCb");
    
    // 导出,否则jsonpCb会被压缩掉不能被识别
    window["jsonpCb"] = jsonpCb;
    

    所有业务代码合并压缩

    a.js

    var getName = function() {return "binnng"};
    

    b.js

    alert(getName());
    

    如果单独压缩a.jsb.js就会出问题,结果会是a.js中什么都没有,b.jsgetName方法未定义(undefined)。正确的做法则是,两者合并再进行压缩。

    结语

    GCC的高级压缩(Advanced)非常强大,对代码压缩做到了极致,但是其对代码书写要求也比较严格,并且破坏性压缩也被很多开发者所诟病。

    但是稍加注意,严格规范自身代码风格,了解GCC压缩方式原理,利用好GCC高级压缩,一定会大大减少JS的体积,从而大幅度的提升前端代码性能。

    另外,GCC像其他压缩工具一样,也有GruntGulp构建组件,可以很方便的去使用它。

    参考

    https://developers.google.com/closure/compiler/ (需翻墙)

  • 相关阅读:
    MySQL5.7的Linux安装shell脚本之二进制安装
    MySQL与Mongo简单的查询 1
    order by 与group by 之间排序问题
    说说左连接出现重复记录的问题
    MySQL5.6的Linux安装shell脚本之二进制安装(一)
    搭建简单FTP服务器以及过程中容易遇到的几个问题(一)
    jqurty
    jquery中的事件与动画
    SQL SERVER数据库设计与现实
    jquery2
  • 原文地址:https://www.cnblogs.com/10manongit/p/12978203.html
Copyright © 2011-2022 走看看