在服務端合併和壓縮JavaScript和CSS文件

Web性能優化最佳實踐中最重要的一條是減少HTTP請求,它也是YSlow中比重最大的一條規則。減少HTTP請求的方案主要有合併JavaScript和CSS文件、CSS Sprites圖像映射(Image Map)使用Data URI來編碼圖片。CSS Sprites和圖像映射現在已經隨處可見了,但由於IE6和IE7不支持Data URI以及性能問題,這項技術尚未大量使用。目前大部分網頁中的JavaScript和CSS文件數量和開發時一致,少量的網頁會根據實際情況採取本地合 並,這些合併中相當多的是有選擇地手動完成,每次新的合併都需要重新在本地完成並上傳到服務器,比較的隨意和繁瑣,同樣文件的壓縮也有類似的情況。而利用 服務端的合併和壓縮,我們就可以按照開發的邏輯儘可能讓文件的顆粒度變小,利用網頁中URL的規則來自動實現文件的合併和壓縮,這會相當的靈活和高效。 

YUI Combo Handler

2008年7月YUI Team宣佈在YAHOO! CDN上對YUI JavaScript組件提供Combo Handler服務。Combo Handler是Yahoo!開發的一個Apache模塊,它實現了開發人員簡單方便地通過URL來合併JavaScript和CSS文件,從而大大減少文件請求數。比如在頁面上使用YUI2的Rich Text Editor組件需要引入多個JavaScript文件,常用方式如下:

<script src="http://yui.yahooapis.com/2.8.0r4/build/yahoo-dom-event/ yahoo-dom-event.js"></script> <script src="http://yui.yahooapis.com/2.8.0r4/build/container/ container_core-min.js"></script> <script src="http://yui.yahooapis.com/2.8.0r4/build/menu/menu-min.js"></script> <script src="http://yui.yahooapis.com/2.8.0r4/build/element/element-min.js"></script> <script src="http://yui.yahooapis.com/2.8.0r4/build/button/button-min.js"></script> <script src="http://yui.yahooapis.com/2.8.0r4/build/editor/editor-min.js"></script> 

而使用Combo Handler服務之後,則上述的代碼可以寫爲:

<script src="http://yui.yahooapis.com/combo? 2.8.0r4/build/yahoo-dom-event/yahoo-dom-event.js& 2.8.0r4/build/container/container_core-min.js& 2.8.0r4/build/menu/menu-min.js& 2.8.0r4/build/element/element-min.js& 2.8.0r4/build/button/button-min.js& 2.8.0r4/build/editor/editor-min.js"></script>

除了代碼的可讀性稍稍有一點點降低外,使用Combo Handler服務大大的降低了HTTP請求數,同時也減少了URL代碼量,這對於Web性能優化來講至關重要。所以,隨後YUI從2.6.0開始,其核心組件YUI Loader內置了Combo Handling功能,即使用YUI Loader時,通過配置combine屬性就可以把要加載的多個JavaScript或CSS文件按照使用Combo Handler服務的形式合併起來,這時只要靜態文件的服務器支持Combo Handler就行了。在YUI中當combine配置爲true時,CDN默認是使用Yahoo! CDN(http://yui.yahooapis.com),所以沒有任何問題。這正是YUI最迷人的地方之一。

遺憾的是http://yui.yahooapis.com在中國的速度並不佳,本來中國雅虎提供了http://cn.yui.yahooapis.com/ ,但尚未提供Combo Handler服務,同時因種種原因,其更新在YUI 2.7.0之後就停滯了。更糟糕的是Yahoo!開發的支持Combo Handler的Apache模塊雖然據傳有計劃開源,但至少現在依舊是私有技術,要使用就需要自己實現類似功能,所以國內類似技術的應用並不太多。

Minify

在Google Code上有一個PHP的開源項目叫Minify, 它可以合併、精簡、Gzip壓縮和緩存JavaScript和CSS文件。其文件合併功能就非常類似Combo Handler,只不過URL的語法稍微有點不同。如果Yahoo! CDN安裝了Minify,那麼上面Rich Text Editor的代碼用Minify的默認格式來寫就是:

<script src="http://yui.yahooapis.com/min/f= 2.8.0r4/build/yahoo-dom-event/yahoo-dom-event.js, 2.8.0r4/build/container/container_core-min.js, 2.8.0r4/build/menu/menu-min.js, 2.8.0r4/build/element/element-min.js, 2.8.0r4/build/button/button-min.js, 2.8.0r4/build/editor/editor-min.js"></script>

本地使用Minify很簡單,只需要Apache + PHP環境就OK了:

  1. 安裝好Apache + PHP (WindowsMac)。
  2. 下載Minify源碼,解壓,然後把min文件夾複製到指定的根目錄下,比如localhost。這時URL的寫法大概是http://localhost/min/f=...
  3. 啓用Apache的Mod Rewrite模塊,然後在min文件夾下新建.htaccess文件,並添加如下Rewrite規則
    <IfModule mod_rewrite.c> RewriteEngine on  # You may need RewriteBase on some servers # 如果做了所有的開啓Mod Rewrite的設置依舊無效,請城市嘗試啓用下面這句 #RewriteBase /min  # rewrite URLs like "/min/f=..." to "/min/?f=..." RewriteRule ^([bfg]=.*)  index.php?$1 [L,NE] </IfModule>

    如果不啓用Mod Rewrite功能,則Minify的URL會類似http://localhost/min/index.php?f=…,這對客戶端和中間服務器的緩存不利,而啓用了Mod Rewrite之後的URL類似http://localhost/min/f=…,不僅解決前面問題且更短。

  4. 配置Minify,即編輯min/config.php文件
    $min_enableBuilder = true; //本地使用時可以通過http://dwn/min/builder/來進行配置,外部使用時請設置爲false  //$min_cachePath = 'c:\\WINDOWS\\Temp'; //$min_cachePath = '/tmp'; //$min_cachePath = preg_replace('/^\\d+;/', '', session_save_path()); //選擇其一,去掉註釋設置臨時緩存目錄,這樣可以減少程序運算提高性能  $min_serveOptions['maxAge'] = 1800; //設置瀏覽器緩存的時間,爲了提升性能建議這個時間設置儘可能的長,比如315360000 //如果需要在不改變URL的情況下更新靜態文件,可以採用類似時間戳的方式, //如http://localhost/min/f=example/example.css&20100601.css //建議靜態文件採用版本號管理,每次修改都需要升級版本號,這樣就無需時間戳了, //如http://localhost/min/f=example/example_1_0_1.css  $min_serveOptions['minApp']['maxFiles'] = 10; //參數f獲取參數的個數,即合併的文件個數,這個數量完全可以增大,比如50, //當然可能會遇到URL最大值問題,後會有解釋  $min_documentRoot = ''; //$min_documentRoot = substr(__FILE__, 0, strlen(__FILE__) - 15); //$min_documentRoot = $_SERVER['SUBDOMAIN_DOCUMENT_ROOT']; //當$min_documentRoot爲空時,其值就是$_SERVER['DOCUMENT_ROOT'], //但合併的文件不在$_SERVER['DOCUMENT_ROOT']下,會導致400錯誤, //這個時候可以啓用第2行或第3行  
  5. 使用Minify

    比如,有兩個JavaScript文件,http://localhost/example/a.js,http://localhost/example/b.js,那麼使用Minify合併的URL是http://localhost/min/f=/example/a.js,/example/b.js,直接把這個URL放到頁面中就可以使用了。

實際上Minify不僅僅實現了合併功能,同時默認在合併的同時還會對文件進行精簡壓縮,如果你在本地本身就對文件進行壓縮了,比如使用YUI Compressor,那麼可以在config.php中進行如下設置取消Minify的壓縮以提升性能:

$min_serveOptions['minifiers']['application/x-javascript'] = ''; $min_serveOptions['minifiers']['text/css'] = ''; 

如果服務端支持Java,那麼也可以對Minify進行簡單配置而實現利用YUI Compressor壓縮JavaScript和CSS文件

直接在服務端進行合併和壓縮,這非常的靈活,也極大的減輕了前端開發成果的部署過程,真使事半功倍。更多配置請看Minify CookBookWiki

小提示:Minify使用的New BSD License, 這就意味着使用者可以幾乎自由的使用該代碼。BSD協議鼓勵代碼共享,但需要尊重代碼作者的著作權。BSD由於允許使用者修改和重新發布代碼,也允許使用 或在BSD代碼上開發商業軟件發佈和銷售,因此是對商業集成很友好的協議。而很多的公司企業在選用開源產品的時候都首選BSD協議,因爲可以完全控制這些 第三方的代碼,在必要的時候可以修改或者二次開發。更多中文信息請看《BSD協議》

在YUI3中使用Minify

在YUI2中,合併機制只支持庫本身的文件,自定義的文件會單獨一一加載。從YUI3開始,模塊變得更小,這樣就導致使用合併時URL會變長,但在IE下URL的最大值是2083,Apache默認的URL最大值是8192,所以當URL在對應瀏覽器下超出最大值時,YUI3會自動根據瀏覽器判斷進行拆分成多個合併的URL,並且還提供了maxURLLength來設置最大值。而從YUI3.1.0開始,不僅僅支持自定義文件的合併,還支持可以使用多個提供合併服務的CDN,即可以對YUI組件使用http://yui.yahooapis.com這個CDN,其餘文件使用其他支持合併的CDN,這樣非常的實用、方便和靈活。示例在YUI3中使用Minify就說明了這點。

由於YUI默認URL的合併形式和Minify的不相同,所以在YUI實例化時需要利用正則替換來實現YUI3支持Minify的URL合併形式,但這種方式既不靈活,且有風險,不友好又效率低。比較簡單的方式是直接修改YUI 3的源碼,如示例在修改後的YUI3中使用Minify。同時,YUI 3.1.*的版本有一個bug,即同時合併JavaScript和CSS時,較短的那個URL結尾處多一個&符號,如示例在YUI3中使用Minify中:

http://yui.yahooapis.com/combo?3.1.1/build/widget/assets/skins/sam/widget.css& 3.1.1/build/console/assets/skins/sam/console.css&  http://dancewithnet.com/min/b=yui&f=3.1.1/tabview/assets/skins/sam/tabview.css&

這兩種都可以使用,雖然在早期的IE瀏覽器版本(如IE6)中會有無法解析的風險,且影響某些特定情況下的緩存,但當使用修改後的YUI時,合併的URL變成類似/min/b=yui&f=3.1.1/tabview/assets/skins/sam/tabview.css,的樣子,就會出現bug了。對於YUI的Combo Handler來說這是一個小bug,所以YUI3把這個bug設置爲P5。但當我們改造YUI3來更好的支持Minify時,還要解決這個問題,具體方案請看示例在修改後的YUI3中使用Minify

在CDN上使用Minify

CDN的全稱是Content Delivery Network,即內容分發網絡。其最常應用就是通過位於不同地理位置的服務器把靜態資源部署到用戶最近的邊緣,這樣能有效解決Web服務中大量靜態資源 的速度和性能問題。由於實施成本比較高,所以在實際的應用中,大型公司一般會組建自己的CDN,而小型公司只能租借第三方的CDN,但不管怎樣這兩種方式 都不會影響在服務端實施合併和壓縮程序。一般情況下,靜態資源也並不是直接上傳到CDN,而是先傳到一臺後臺服務器,然後各地CDN的前端Cache服務 器按需索取。YUI CDN的Combo Handler就是部署在其後臺服務器上的,Minify也應如此。簡單圖示如下:

當一個資源請求到CDN時,CDN會先檢查本地是否存在這個資源,如果有則會直接返回該資源,如果沒有則會請求其後臺服務器,後臺服務器會依據資源 URL的信息進行相應的處理,然後返回給CDN,CDN就會存儲這個資源,再次出現這個資源請求時就無需請求後臺服務器了。所以,雖然合併特別是壓縮 JavaScript和CSS文件是消耗時間的,但是由於只需要第一次,並且第一次基本上由我們自己訪問掉(我們可以創建程序自動進行一次訪問來保證,實 際上圖片優化也可以採用這種方式),所以基本上很安全。這正是目前在口碑網實施的JavaScript和CSS合併方案,在第4屆D2的《前端性能優化和自動化》中也介紹了它。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章