一、背景
前端開發中,特別是移動端,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.js
和b.js
就會出問題,結果會是a.js
中什麼都沒有,b.js
中getName
方法未定義(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像其他壓縮工具一樣,也有Grunt
、Gulp
構建元件,可以很方便的去使用它。
轉載自:
1)https://segmentfault.com/a/1190000002575760
2)https://blog.csdn.net/nh18304030935hn/article/details/54571682