一、背景

前端開發中,特別是移動端,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三種執行方式

1)、”Closure Compiler Service UI”:GCC提供的線上壓縮方式,只需匯入檔案路徑或是直接複製貼上檔案內容,即可實現壓縮。線上壓縮網站為:http://closure-compiler.appspot.com/home詳細步驟如下:

A.開啟頁面,在”Add a URL”文字框中輸入檔案路徑,然後點選”add”。或者直接把檔案內容複製到下邊的檔案域中。

B.在”Optimization”中選擇壓縮模式,預設為”Simple”模式。

C.點選”Compile”按鈕,進行壓縮。如圖,右側這顯示壓縮完的檔案資訊。

D.可以選擇複製下面的文字內容貼上到壓縮前原檔案,或者右鍵點選”default.js”-->”連結另存為”,直接將壓縮完的檔案重新命名並儲存到本地。

2)、”Closure Compiler Service API”:通過自定義HTML頁面傳送請求的方式來獲取壓縮檔案。與”Closure Compiler Service UI”相比,它可以建立自己的工具,建立自己的工作流。而”Closure Compiler Service UI”只適合壓縮少量的程式碼和檔案。詳細步驟如下:

A.建立一個名為”closure_compiler_test.html”的HTML頁面,用於傳送post請求。程式碼如下:

 <html>

  <body>

    <form action="http://closure-compiler.appspot.com/compile" method="POST">

    <p>Type JavaScript code to optimize here:</p>

    <textarea name="js_code" cols="50" rows="5">

    function hello(name) {

      // Greets the user

      alert('Hello, ' + name);

    }

    hello('New user');

    </textarea>

    <input type="hidden" name="compilation_level" value="WHITESPACE_ONLY">

    <input type="hidden" name="output_format" value="text">

    <input type="hidden" name="output_info" value="compiled_code">

    <br><br>

    <input type="submit" value="Optimize">

    </form>

  </body>

</html>

B.用瀏覽器開啟該HTML檔案,點選”Optimize”按鈕提交,並會返回壓縮後文件。然後複製貼上儲存。

注意:每個請求至少必須傳送以下引數:

a.”js_code”或”code_url”:這個引數的值指示要編譯的JavaScript。必須至少包含其中一個引數,並且可以同時包括。”js_code”必須是包含javascript的字串。”code_url”必須是包含js檔案的url地址。

b.”compilation_level”:這個引數表示檔案壓縮的模式,有三個值,分別為”whitespace_only”,”simple_optimizations”,”advanced_optimizations”。上文用的是”whitespace_only”,預設為”simple_optimizations”。

c.”output_info”:此引數的值指示要從編譯器獲取的資訊型別。有四個值,分別為”compiled_code”,”warnings”,”errors”,”statistics”。上文用的是”compiled_code”,返回的是壓縮好的檔案。

d.”output_format”:此引數表示輸出的格式。有三個值,分別為”text”,”json”,”xml”,預設為”text”。

  更多關於API的使用請參照官網。

3)、”Closure Compiler Application”:用官網提供的客戶端進行程式碼壓縮。從官網下載壓縮包,地址為:https://dl.google.com/closure-compiler/compiler-latest.zip。本客戶端是在Java環境下執行的,所以下載之前應安裝JDK。安裝JDK步驟參考:http://jingyan.baidu.com/article/2d5afd6993a6db85a2e28e9f.html

詳細步驟如下:

A.建立一個名為”closure-compiler”的資料夾。

B.把下載的壓縮包解壓到”closure-compiler”下。

C.把要壓縮的js檔案也放到當前目錄下。

D.用命令視窗開啟資料夾中”README.md”檔案。開啟如下

系統會自動進入到當前檔案位置。如果沒有開啟,可以嘗試如下做法:

a.按”windows+r”鍵,進入“執行”,輸入”cmd”,進入命令視窗。

b.然後輸入剛剛建立的”closure-compiler”資料夾的路徑。例:”F:\workspace\closure-compiler”,則如下:

E.然後輸入”java -jar 目錄下.jar檔案--js 目錄下.js檔案--js_output_file 壓縮完儲存的檔名”,例:”java -jar closure-compiler-v20161201.jar --js setting.js --js_output_file setting.min.js”,生成的檔案也會儲存在當前的目錄下(closure-compiler資料夾)。生成的檔案的壓縮模式預設為”SIMPLE”,如果要改變其他的壓縮模式,在上邊的的語句中新增”--compilation_level=ADVANCED”,值可以為”WHITESPACE_ONLY”,”SIMPLE”,”ADVANCED”。

如果想了解更多使用方法,請輸入”java -jar closure-compiler-v20161201.jar --help”。

四、結語

GCC的高階壓縮(Advanced)非常強大,對程式碼壓縮做到了極致,但是其對程式碼書寫要求也比較嚴格,並且破壞性壓縮也被很多開發者所詬病。

但是稍加註意,嚴格規範自身程式碼風格,瞭解GCC壓縮方式原理,利用好GCC高階壓縮,一定會大大減少JS的體積,從而大幅度的提升前端程式碼效能。

另外,GCC像其他壓縮工具一樣,也有GruntGulp構建元件,可以很方便的去使用它。

轉載自:

  1)https://segmentfault.com/a/1190000002575760

  2)https://blog.csdn.net/nh18304030935hn/article/details/54571682