在服務(wù)端合并和壓縮JavaScript和CSS文件
Web性能優(yōu)化最佳實(shí)踐中最重要的一條是減少HTTP請(qǐng)求,它也是YSlow中比重最大的一條規(guī)則。減少HTTP請(qǐng)求的方案主要有合并JavaScript和CSS文件、CSS Sprites、圖像映射(Image Map)和使用Data URI來編碼圖片。CSS Sprites和圖像映射現(xiàn)在已經(jīng)隨處可見了,但由于IE6和IE7不支持Data URI以及性能問題,這項(xiàng)技術(shù)尚未大量使用。
目前大部分網(wǎng)頁中的JavaScript和CSS文件數(shù)量和開發(fā)時(shí)一致,少量的網(wǎng)頁會(huì)根據(jù)實(shí)際情況采取本地合并,這些合并中相當(dāng)多的是有選擇地手動(dòng)完成,每次新的合并都需要重新在本地完成并上傳到服務(wù)器,比較的隨意和繁瑣,同樣文件的壓縮也有類似的情況。而利用服務(wù)端的合并和壓縮,我們就可以按照開發(fā)的邏輯盡可能讓文件的顆粒度變小,利用網(wǎng)頁中URL的規(guī)則來自動(dòng)實(shí)現(xiàn)文件的合并和壓縮,這會(huì)相當(dāng)?shù)撵`活和高效。
YUI Combo Handler
2008年7月YUI Team宣布在YAHOO! CDN上對(duì)YUI JavaScript組件提供Combo Handler服務(wù)。Combo Handler是Yahoo!開發(fā)的一個(gè)Apache模塊,它實(shí)現(xiàn)了開發(fā)人員簡(jiǎn)單方便地通過URL來合并JavaScript和CSS文件,從而大大減少文件請(qǐng)求數(shù)。比如在頁面上使用YUI2的Rich Text Editor組件需要引入多個(gè)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服務(wù)之后,則上述的代碼可以寫為:
- <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>
除了代碼的可讀性稍稍有一點(diǎn)點(diǎn)降低外,使用Combo Handler服務(wù)大大的降低了HTTP請(qǐng)求數(shù),同時(shí)也減少了URL代碼量,這對(duì)于Web性能優(yōu)化來講至關(guān)重要。所以,隨后YUI從2.6.0開始,其核心組件YUI Loader內(nèi)置了Combo Handling功能,即使用YUI Loader時(shí),通過配置combine屬性就可以把要加載的多個(gè)JavaScript或CSS文件按照使用Combo Handler服務(wù)的形式合并起來,這時(shí)只要靜態(tài)文件的服務(wù)器支持Combo Handler就行了。
在YUI中當(dāng)combine配置為true時(shí),CDN默認(rèn)是使用Yahoo! CDN(http://yui.yahooapis.com),所以沒有任何問題。這正是YUI最迷人的地方之一。
遺憾的是http://yui.yahooapis.com在中國(guó)的速度并不佳,本來中國(guó)雅虎提供了http://cn.yui.yahooapis.com/ ,但尚未提供Combo Handler服務(wù),同時(shí)因種種原因,其更新在YUI 2.7.0之后就停滯了。更糟糕的是Yahoo!開發(fā)的支持Combo Handler的Apache模塊雖然據(jù)傳有計(jì)劃開源,但至少現(xiàn)在依舊是私有技術(shù),要使用就需要自己實(shí)現(xiàn)類似功能,所以國(guó)內(nèi)類似技術(shù)的應(yīng)用并不太多。
Minify
在Google Code上有一個(gè)PHP的開源項(xiàng)目叫Minify,它可以合并、精簡(jiǎn)、Gzip壓縮和緩存JavaScript和CSS文件。其文件合并功能就非常類似Combo Handler,只不過URL的語法稍微有點(diǎn)不同。如果Yahoo! CDN安裝了Minify,那么上面Rich Text Editor的代碼用Minify的默認(rèn)格式來寫就是:
- <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很簡(jiǎn)單,只需要Apache + PHP環(huán)境就OK了:
1.安裝好Apache + PHP (Windows、Mac)。
2.下載Minify源碼,解壓,然后把min文件夾復(fù)制到指定的根目錄下,比如localhost。這時(shí)URL的寫法大概是http://localhost/min/f=...
3.啟用Apache的Mod Rewrite模塊,然后在min文件夾下新建.htaccess文件,并添加如下Rewrite規(guī)則:
- <IfModule mod_rewrite.c>
- RewriteEngine on
- # You may need RewriteBase on some servers
- # 如果做了所有的開啟Mod Rewrite的設(shè)置依舊無效,請(qǐng)城市嘗試啟用下面這句
- #RewriteBase /min
- # rewrite URLs like "/min/f=..." to "/min/?f=..."
- RewriteRule ^([bfg]=.*) index.php?$1 [L,NE]
- </IfModule>
如果不啟用Mod Rewrite功能,則Minify的URL會(huì)類似http://localhost/min/index.php?f=…,這對(duì)客戶端和中間服務(wù)器的緩存不利,而啟用了Mod Rewrite之后的URL類似http://localhost/min/f=…,不僅解決前面問題且更短。
4.配置Minify,即編輯min/config.php文件
- $min_enableBuilder = true;
- //本地使用時(shí)可以通過http://dwn/min/builder/來進(jìn)行配置,外部使用時(shí)請(qǐng)?jiān)O(shè)置為false
- //$min_cachePath = 'c:\\WINDOWS\\Temp';
- //$min_cachePath = '/tmp';
- //$min_cachePath = preg_replace('/^\\d+;/', '', session_save_path());
- //選擇其一,去掉注釋設(shè)置臨時(shí)緩存目錄,這樣可以減少程序運(yùn)算提高性能
- $min_serveOptions['maxAge'] = 1800;
- //設(shè)置瀏覽器緩存的時(shí)間,為了提升性能建議這個(gè)時(shí)間設(shè)置盡可能的長(zhǎng),比如315360000
- //如果需要在不改變URL的情況下更新靜態(tài)文件,可以采用類似時(shí)間戳的方式,
- //如http://localhost/min/f=example/example.css&20100601.css
- //建議靜態(tài)文件采用版本號(hào)管理,每次修改都需要升級(jí)版本號(hào),這樣就無需時(shí)間戳了,
- //如http://localhost/min/f=example/example_1_0_1.css
- $min_serveOptions['minApp']['maxFiles'] = 10;
- //參數(shù)f獲取參數(shù)的個(gè)數(shù),即合并的文件個(gè)數(shù),這個(gè)數(shù)量完全可以增大,比如50,
- //當(dāng)然可能會(huì)遇到URL最大值問題,后會(huì)有解釋
- $min_documentRoot = '';
- //$min_documentRoot = substr(__FILE__, 0, strlen(__FILE__) - 15);
- //$min_documentRoot = $_SERVER['SUBDOMAIN_DOCUMENT_ROOT'];
- //當(dāng)$min_documentRoot為空時(shí),其值就是$_SERVER['DOCUMENT_ROOT'],
- //但合并的文件不在$_SERVER['DOCUMENT_ROOT']下,會(huì)導(dǎo)致400錯(cuò)誤,
- //這個(gè)時(shí)候可以啟用第2行或第3行
5.使用Minify
比如,有兩個(gè)JavaScript文件,http://localhost/example/a.js,http://localhost/example/b.js,那么使用Minify合并的URL是http://localhost/min/f=/example/a.js,/example/b.js,直接把這個(gè)URL放到頁面中就可以使用了。
實(shí)際上Minify不僅僅實(shí)現(xiàn)了合并功能,同時(shí)默認(rèn)在合并的同時(shí)還會(huì)對(duì)文件進(jìn)行精簡(jiǎn)壓縮,如果你在本地本身就對(duì)文件進(jìn)行壓縮了,比如使用YUI Compressor,那么可以在config.php中進(jìn)行如下設(shè)置取消Minify的壓縮以提升性能:
$min_serveOptions['minifiers']['application/x-javascript'] = '';
$min_serveOptions['minifiers']['text/css'] = '';
如果服務(wù)端支持Java,那么也可以對(duì)Minify進(jìn)行簡(jiǎn)單配置而實(shí)現(xiàn)利用YUI Compressor壓縮JavaScript和CSS文件。
直接在服務(wù)端進(jìn)行合并和壓縮,這非常的靈活,也極大的減輕了前端開發(fā)成果的部署過程,真使事半功倍。
小提示:Minify使用的New BSD License,這就意味著使用者可以幾乎自由的使用該代碼。BSD協(xié)議鼓勵(lì)代碼共享,但需要尊重代碼作者的著作權(quán)。BSD由于允許使用者修改和重新發(fā)布代碼,也允許使用或在BSD代碼上開發(fā)商業(yè)軟件發(fā)布和銷售,因此是對(duì)商業(yè)集成很友好的協(xié)議。
而很多的公司企業(yè)在選用開源產(chǎn)品的時(shí)候都首選BSD協(xié)議,因?yàn)榭梢酝耆刂七@些第三方的代碼,在必要的時(shí)候可以修改或者二次開發(fā)。更多中文信息請(qǐng)看《BSD協(xié)議》。
在YUI3中使用Minify
在YUI2中,合并機(jī)制只支持庫本身的文件,自定義的文件會(huì)單獨(dú)一一加載。從YUI3開始,模塊變得更小,這樣就導(dǎo)致使用合并時(shí)URL會(huì)變長(zhǎng),但在IE下URL的最大值是2083,Apache默認(rèn)的URL最大值是8192,所以當(dāng)URL在對(duì)應(yīng)瀏覽器下超出最大值時(shí),YUI3會(huì)自動(dòng)根據(jù)瀏覽器判斷進(jìn)行拆分成多個(gè)合并的URL,并且還提供了maxURLLength來設(shè)置最大值。
而從YUI3.1.0開始,不僅僅支持自定義文件的合并,還支持可以使用多個(gè)提供合并服務(wù)的CDN,即可以對(duì)YUI組件使用http://yui.yahooapis.com這個(gè)CDN,其余文件使用其他支持合并的CDN,這樣非常的實(shí)用、方便和靈活。示例在YUI3中使用Minify就說明了這點(diǎn)。
由于YUI默認(rèn)URL的合并形式和Minify的不相同,所以在YUI實(shí)例化時(shí)需要利用正則替換來實(shí)現(xiàn)YUI3支持Minify的URL合并形式,但這種方式既不靈活,且有風(fēng)險(xiǎn),不友好又效率低。
比較簡(jiǎn)單的方式是直接修改YUI 3的源碼,如示例在修改后的YUI3中使用Minify。同時(shí),YUI 3.1.*的版本有一個(gè)bug,即同時(shí)合并JavaScript和CSS時(shí),較短的那個(gè)URL結(jié)尾處多一個(gè)&符號(hào),如示例在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)中會(huì)有無法解析的風(fēng)險(xiǎn),且影響某些特定情況下的緩存,但當(dāng)使用修改后的YUI時(shí),合并的URL變成類似/min/b=yui&f=3.1.1/tabview/assets/skins/sam/tabview.css,的樣子,就會(huì)出現(xiàn)bug了。對(duì)于YUI的Combo Handler來說這是一個(gè)小bug,所以YUI3把這個(gè)bug設(shè)置為P5。但當(dāng)我們改造YUI3來更好的支持Minify時(shí),還要解決這個(gè)問題,具體方案請(qǐng)看示例在修改后的YUI3中使用Minify。
在CDN上使用Minify
CDN的全稱是Content Delivery Network,即內(nèi)容分發(fā)網(wǎng)絡(luò)。其最常應(yīng)用就是通過位于不同地理位置的服務(wù)器把靜態(tài)資源部署到用戶最近的邊緣,這樣能有效解決Web服務(wù)中大量靜態(tài)資源的速度和性能問題。
由于實(shí)施成本比較高,所以在實(shí)際的應(yīng)用中,大型公司一般會(huì)組建自己的CDN,而小型公司只能租借第三方的CDN,但不管怎樣這兩種方式都不會(huì)影響在服務(wù)端實(shí)施合并和壓縮程序。一般情況下,靜態(tài)資源也并不是直接上傳到CDN,而是先傳到一臺(tái)后臺(tái)服務(wù)器,然后各地CDN的前端Cache服務(wù)器按需索取。YUI CDN的Combo Handler就是部署在其后臺(tái)服務(wù)器上的,Minify也應(yīng)如此。簡(jiǎn)單圖示如下:
當(dāng)一個(gè)資源請(qǐng)求到CDN時(shí),CDN會(huì)先檢查本地是否存在這個(gè)資源,如果有則會(huì)直接返回該資源,如果沒有則會(huì)請(qǐng)求其后臺(tái)服務(wù)器,后臺(tái)服務(wù)器會(huì)依據(jù)資源URL的信息進(jìn)行相應(yīng)的處理,然后返回給CDN,CDN就會(huì)存儲(chǔ)這個(gè)資源,再次出現(xiàn)這個(gè)資源請(qǐng)求時(shí)就無需請(qǐng)求后臺(tái)服務(wù)器了。
所以,雖然合并特別是壓縮JavaScript和CSS文件是消耗時(shí)間的,但是由于只需要第一次,并且第一次基本上由我們自己訪問掉(我們可以創(chuàng)建程序自動(dòng)進(jìn)行一次訪問來保證,實(shí)際上圖片優(yōu)化也可以采用這種方式),所以基本上很安全。這正是目前在口碑網(wǎng)實(shí)施的JavaScript和CSS合并方案,在第4屆D2的《前端性能優(yōu)化和自動(dòng)化》中也介紹了它。
原文地址:http://dancewithnet.com/2010/06/08/minify-js-and-css-files-in-server/
【編輯推薦】