使用Google Closure Compiler高级压缩Javascript代码注意的几个地方

介绍

GCC(Google Closure Compiler)是由谷歌发布的Js代码压缩编译工具。它能够作到分析Js的代码,移除不须要的代码(dead code),而且去重写它,最后再进行压缩。javascript

三种压缩模式

GCC提供三种压缩模式:css

1)Whitespace only
2)Simple
3)Advanced

咱们以这段简单的代码为例前端

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

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

Whitespace only

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

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

Simple

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

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

Advanced

alert("Hello, binnng"); 

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

注意的地方

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

如下全部未指名级别的GCC压缩均为Advanced级别。api

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构建组件,能够很方便的去使用它。

 

//原文地址:http://segmentfault.com/blog/laopopo/1190000002575760

相关文章
相关标签/搜索