一、背景
前端開發中,特別是移動端,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