使用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

相關文章
相關標籤/搜索