談前端代碼的精簡、混淆、壓縮和編譯

原文地址:http://www.vinqon.com/codeblog/?detail/11106javascript

 前幾天忽然想寫一個css的js壓縮工具,因而這兩天研究了一下幾個js、css的壓縮工具而且理清楚了一些概念和原理,下面總結一下。css

 
幾個基本概念
在網站部署前,咱們每每要對前端的代碼進行發佈,我這裏說的「發佈」,指的就是精簡、混淆、壓縮、編譯或者還有其餘的操做,有些操做很類似,但每一個操做的都有其中的意義。
 

精簡(minify)

對前端代碼精簡的目的很明顯,就是減小代碼體積,減少網絡傳輸時間,提升頁面響應。
而具體到如何精簡,其實也很簡單,下面是其中的一些辦法:
1.刪除代碼註釋
2.刪除無心義或者多餘的空白(如空格,製表符,回車,換行)
3.刪除能夠省略的符號(如css最後一條規則後面的分號,js塊內最後的一條語句的分號)
4.縮短語句(若是css的簡寫,html中disabled='disabled' 改爲disabled , js中縮短局部變量)
 
對於精簡這個功能,大部分工具都基本實現了上面的方法,包括有yuicompresser,closure complie,jsmin,packer.
 
 

混淆(obfuscation)

混淆這個功能主要針對Javascript代碼,它的目的是減低代碼的可讀性,防止被追蹤出程序邏輯。
事實上,對代碼精簡,壓縮,編碼都有混淆的效果。
首先,上面提到精簡的辦法中,刪除註釋,刪除縮進(空格,製表符,換行),縮短局部變量均可以有效減低程序可讀性。除了刪除縮進能夠用過js格式化/美化工具還原,其它兩個步驟都是不可逆的。
其次,經過編碼混淆代碼,這篇文章 《javascript經常使用混淆方法》裏面介紹了不少牛X的編碼加密方法。可是這些方法有個明顯缺點,增長代碼體積,並且編碼加密都是可逆的。
最後,經過壓縮的辦法,固然,也是可逆的,下面咱們詳細探討一下。
 
 

壓縮(compress)

壓縮這一個說法很常被用來歸納前面這三種操做,其實上,真正實現壓縮的我目前只看到一種方案: packer的base64編碼壓縮.
這裏能夠先看一個簡單的例子:
壓縮前代碼:
1 document.getElemntById("header").innerHTML="This is the header";
壓縮後代碼:
1 eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String)){while(c--)r[c]=k[c]||c;k=[function(e){returnr[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);returnp}('1.2("0").3="4 5 6 0";',7,7,'header|document|getElemntById|innerHTML|This|is|the'.split('|'),0,{}))
 
壓縮後的代碼很噁心,可是認真研究能夠發現裏面只有三個東西:壓縮後原文,字符表,解壓器。
packer的base64編碼的壓縮率很高,精簡後代碼依然能夠減小50%體積以上,由於帶有解壓器,和字符表,上面的例子沒有體現壓縮效果,通常來講,越長的代碼壓縮率更高。
若是對裏面構造有興趣的能夠直接研究packer的源代碼,算法都是用js寫的,另外,還能夠看看這篇文章 《Packer,你對個人JS作了什麼!》.
不少地方都把packer這個功能稱爲混淆,固然,這的確有混淆的效果,上面也提到。可是,從算法上看,packer base64 encode是一個字典壓縮算法,故這裏歸類爲壓縮。
 
另外,須要提醒的是,雖然壓縮有混淆效果,可是過程依然可逆,並且解壓器和字符代表擺在那裏,只要把eval四個字母改爲alert就能夠看到壓縮前的代碼。
由於用了邪惡的eval,packer後的代碼性能會減低...不少,另外,解壓的過程也會消耗一點時間。
特別要注意的是,若是服務器有gzip功能,就沒必要也不該用packer base64 encode來壓縮。由於packer base64 encode壓縮加gzip壓縮後的體積比源代碼只用gzip壓縮還要大。
緣由也可想而知,咱們用js進行了一次低效壓縮,gzip壓縮的空間就大大減低了。
 

編譯(compile)

說到編譯,不得不先提到 google closure compile,它和其餘工具不一樣的是,除了精簡功能,它還能夠對Javascript代碼進行優化。
gcc的高級模式會對Javascript進行語義分析,而後會進行刪除無用代碼,刪除沒有使用的變量,優化邏輯關係等比較激進的優化。
雖然編譯先後都是Javascript代碼,可是這個過程已經算得上實際意義上的編譯了。
 
另外,除了gcc,還要說的是最近很流行的一些新語言: coffeescriptlesssass,這裏先簡單掃盲一下:
coffeescript是一個類ruby的語言,書寫起來更加簡潔和優美,coffeescript能夠徹底編譯成同效的Javascript。
而less和sass是在css上進行語法擴展,在css上實現了變量,做用域,函數之類的功能。
如今,相似這樣的語言,工具,框架愈來愈多,好像 老趙jscex能夠對線性代碼編譯,不用咱們寫一堆回調。玉伯的seajs能夠「預編譯」模塊,找出模塊依賴關係來異步load順序執行模塊。
github上有一份 list記錄了全部的這類東東,有興趣能夠去研究一下。
 
談到這些,咱們彷彿感受到了前端發展的一個趨勢,咱們原來寫的html,css,Javascript已經開始變成了一個「中間語言」,並且愈來愈多的團隊也有了本身的一套前端編譯系統更加彰顯了這個趨勢。
這是一個有趣的話題,上面不少內容只能貼個連接了,也許下次應該單獨作一篇文章來慢慢討論一下。
 

CSSPacker

這是我研究幾個壓縮工具後,本身突發奇想寫的一個小玩具。
簡單介紹一下,上面提過packer,這是一個真正意義上用Javascript實現的壓縮解壓方案(固然,相對於客戶端的一些壓縮軟件還差很遠),它原本是用來壓縮Javascript的,我把它移植一下,折騰出這個csspacker支持壓縮css。
packer原本有三個功能:精簡代碼,縮短局部變量,base64編碼壓縮,下面簡單介紹一下:
  1. packer本來精簡代碼是根據Javascript語法精簡的,我把精簡代碼部分重寫了,以適應css語言;
  2. 縮短局部變量沒用,直接刪掉這個操做;
  3. base64編碼壓縮適用於任何文本,能夠直接保留。可是解壓的操做要修改一下,原來Javascript直接把解壓後的字符串eval一下就行了,css比比較蛋疼,要新建一個style節點,把解壓後css文本插進去。

另一個比較重大的問題是,圖片路徑問題。css文件上使用的相對路徑是相對於css的位置的,可是js是相對於頁面的位置的,因此,若是css含有相對路徑,要輸入css所在網絡位置,它會自動把裏面的相對路徑轉換爲URL。html

路徑問題讓這個packer不那麼方便了,我還考慮其餘方案。目前的另一個想法是,fork一個分支,把csspacker功能弄進去。
好吧,有興趣的歡迎去調戲調戲一下  csspacker
相關文章
相關標籤/搜索