使用Minify來優(yōu)化網(wǎng)站性能
Minify 是用PHP5開發(fā)的應(yīng)用,通過遵循一些Yahoo的優(yōu)化規(guī)則來提高網(wǎng)站的性能。它會合并多個CSS或者JavaScript文件,移除一些不必要的空格和 注釋,進(jìn)行g(shù)zip壓縮,并且會設(shè)置瀏覽器的緩存頭。Minify 在設(shè)計上和Yahoo的 Combo Handler Service非常像,不過Minify可以合并任何你想要合并的JavaScript和CSS文件。
一般情況下,網(wǎng)站速度的瓶頸都在前端,而最關(guān)鍵的就是資源的加載速度,但是大多數(shù)瀏覽器都有單個域名并發(fā)請求數(shù)限制,所以如果一個頁面中存在很多的 資源,比如CSS和JavaScript文件,那么明顯會降低網(wǎng)站的加載速度,比較好處理方式就是把多個文件通過一個請求來訪問,這樣既不會影響之前的文 件維護(hù),又會減少資源的清楚數(shù)量,Minify就是為之而生。下面是一些被Minify采用的 Yahoo! 優(yōu)化準(zhǔn)則:
2、Add an Expires or a Cache-Control Header
下面兩幅圖分別是啟用Minify之前和啟用Minify之后網(wǎng)站請求時間的一個對比,可以看出啟用Minify之后,資源的加載時間從250ms減少到了125ms,總共節(jié)省了50%的時間。
特性:
合并多個CSS或JavaScript文件為一個文件,減少請求數(shù)量,并且進(jìn)行minify處理
使用了多個開源的庫,包括 JSMin.php ,Minify CSS,Minify HTML
服務(wù)端緩存(fils/APC/Memcache),可以避免不必要的重復(fù)處理
當(dāng)瀏覽器存在資源的緩存,返回HTTP 304 Not Modified
多個文件合并時,自動生成URI
當(dāng)開啟服務(wù)端緩存的時候,在一般的服務(wù)器上Minify每秒可以處理幾百個并發(fā)請求
根據(jù)請求頭,開啟Content-Encoding: gzip。在服務(wù)端緩存開啟的情況下,Minify提供gzipped 文件速度比Apache’s mod_deflate模塊要快
安裝
下載最新的Minify,然后解壓文件到”min” 文件夾
Copy “min” 文件夾到自己網(wǎng)站的DOCUMENT_ROOT下,如果想要Minify在子目錄下工作,看這里
使用
假設(shè)網(wǎng)站域名是http://example.com,Minify安裝在了虛擬主機(jī)的根目錄下,那么訪問http://example.com/min/,我們會看到一個“Minify URI Builder”,我們可以輸入需要進(jìn)行合并的文件URI,如下
點(diǎn)擊Update之后,系統(tǒng)會自動生成一個合并后的URI:/min/?b=js&f=jquery-a.js,jquery- b.js,jquery-c.js。如果覺得URI太長,可以通過配置文件來指定group,編輯min目錄下的groupsConfig.php文件, 加入以下內(nèi)容:
return array( 'js' => array('//js/jquery-a.js', '//js/jquery-b.js', '//js/jquery-c.js'),//相對于DOCUMENT_ROOT ); |
之后就可以通過/min/?g=js來訪問了
Minify在資源首次被請求的時候,會對多個文件進(jìn)行合并,gzip,去除空格,注釋等處理,然后會把處理的結(jié)果進(jìn)行緩存,默認(rèn)情況下是進(jìn)行文件緩存,緩存的key以minify_開頭,修改min/config.php文件,配置緩存文件存放的位置:
$min_cachePath = '/tmp';
除了通過文件進(jìn)行緩存之外,Minify還支持Memcache緩存,修改min/index.php文件,加入以下代碼:
require 'lib/Minify/Cache/Memcache.php'; $memcache = new Memcache; $memcache->connect('localhost', 11211); $min_cachePath = new Minify_Cache_Memcache($memcache); |
Minify支持兩種debug方式,一種是通過firephp調(diào)試PHP錯誤,修改min/config.php文件,加入以下代碼:
$min_errorLogger = true; |
另一種是通過在URL中加入flag進(jìn)行錯誤調(diào)試,在min/config.php中加入
$min_allowDebugFlag = true; |
之后就能以http://example.com/min/f=jquery-a.js,jquery-b.js,jquery-c.js&debug=1方式進(jìn)行調(diào)試了
參考:
http://code.google.com/p/minify/wiki/UserGuide
http://www.mrclay.org/2008/09/19/minify-21-on-mrclayorg/
把min目錄上傳根目錄,根目錄打開http://example.com/min/
Note: Please set $min_cachePath in /min/config.php to improve performance.
設(shè)置/min/config.php文件 ,$min_cachePath 有3個選擇。
//$min_cachePath = ‘c:\\WINDOWS\\Temp’;
//$min_cachePath = ‘/tmp’;
//$min_cachePath = preg_replace(‘/^\\d+;/’, ”, session_save_path());
選擇第2個,去除// .設(shè)置tmp屬性777
在顯示的界面中加入你想合并壓縮的 js/css 路徑,點(diǎn)擊 ‘Update’ 之后會為你生成一個 url,如:http://localhost/min/b=googletesting/js& f=mootools.js,iAction.js,iAjax.js,global.js
css和JS分別合并,2個地址。如果需要組合的文件很多,url 就會變得很長,Minify 支持 group,可以將這些文件分組,這樣 url 中只需指定 g=group名字 就可以了。
安裝完畢后刪除min/builder/index.php 文件。防止其他人登陸!后期如需編輯再次上傳!
示例:
上面的示例中,將三個css文件壓縮為一個css文件,減少了兩個HTTP請求,性能上確實(shí)能提高不少,特別是對于訪問量大的頁面。但是之前一直在思索兩個問題:
1.文件名中包含不規(guī)則的字符,在windows操作系統(tǒng)下是不允許作為文件名的。
2.瀏覽器的緩存怎么處理。
通過一些時間摸索和 跟同事進(jìn)行探討,迷霧漸漸清晰了。對于一個問題,服務(wù)器端是進(jìn)行了urlrewrite了,比如上面圖示的url鏈接其實(shí)是障眼法,是給別人看是這么個東 東(它包含了一些版本和文件名的信息,使用版本號的一個好處就是可以在版本升級的時候可以及時更新緩存,保持更行的css文件的及時生效),其實(shí)是服務(wù)器 端會將它rewrite到另外一個文件里。這個文件是會自動通過參數(shù)整合三個css文件到一個css文件里,最后download到客戶端。那么對于第二 個問題也就好解釋了,url不變,瀏覽器就會從緩存里讀取。
問題想清楚了,偶也就開始思索著自己去怎么實(shí)現(xiàn)這么個功能,下面是我摸索過程中所想到的辦法(經(jīng)測試,都不太理想):
第一:通過服務(wù)端的一個程序文件(這里是PHP的一個小程序)來獲取客戶端發(fā)送的幾個javascript或者CSS的URL信息,之后通 過PHP腳本將這幾個文件整合為一個文件并把整合后的文件發(fā)送到客戶端,在客戶端再通過responseText把代碼動態(tài)生成script或者 style,插入到head中。
<?php
//////////////// test.php file: ////////////////
//file path param
$path=$_GET["jspath"];
//split param
$split=$_GET["split"];
//file path array
$path_arr=split($split,$path);
//store all files text
$js_str="";
//the count of file path array
$count=count($path_arr);
//read each file
for($i=0;$i<$count;$i++){
$file_read=fopen($path_arr[$i],"r");
while(!feof($file_read)){
$line=fgets($file_read);
if(strlen($line) ===1){
$js_str=$js_str.$line."\n";
}else{
$js_str=$js_str.$line;
}
}
fclose($file_read);
}
echo($js_str); // flush all content to client.
?>
///////////////// combine.js file /////////////////
(function(global){
var XHR=function(){
var xhr=false;
if(window.XMLHttpRequest){
xhr=new XMLHttpRequest();
}else{
try{
xhr=new ActiveXObject("Msxml2.XMLHTTP");
}catch(e){
xhr=new ActiveXObject("Microsoft.XMLHTTP");
}
}
return xhr;
}
var InsertElement={
"js":function(text){
var script=document.createElement("script");
script.setAttribute("type","text/javascript");
script.text=text;
document.getElementsByTagName("head")[0].appendChild(script);
},
"javascript":this["js"],
"css":function(text){
var style=document.createElement("style");
style.setAttribute("type","text/css");
style.innerHTML=text;
document.getElementsByTagName("head")[0].appendChild(style);
}
}
var combineJS=function(urls,path,type,split,compress){
split = split || ",";
type = type || "js";
var url=path+"?jspath="+urls.join(split)+"&split="+split;
var xhr=XHR();
xhr.open("GET",url,true);
xhr.onreadystatechange=function(){
if(xhr.readyState === 4 && xhr.status === 200){
InsertElement[type](xhr.responseText);
}
}
xhr.send(null);
}
global.combineJS=combineJS;
})(window);
//combine files
combineJS(["test1.js","test2.js","test3.js","http://localhost/sizzle.js"],"combine.php");
上面的方式是通過動態(tài)生成script或者style的方式來達(dá)到合并文件,減少HTTP請求的目的。但是測試中發(fā)現(xiàn)一個問題:客戶端請求合并后的 文件會等待比較長的時間,這跟直接鏈入幾個文件比起來,消耗的時間差距比較大。如下圖示顯示的合并后的文件插入到了文檔中,以及一直處于等待狀態(tài)的一個問 題(1s~6s之間)。《測試用例》
第二:合并幾個文件的方法和上面的一樣,只是我將合并后的文件內(nèi)容返回來的時候插入到當(dāng)前發(fā)送請求的script標(biāo)簽內(nèi),這得益于一個技 巧:在加載的時候,當(dāng)前的script元素永遠(yuǎn)是處于scripts.length-1的索引位置。因此可以在js代碼里面進(jìn)行改寫:《測試用例》
(function(global){
var XHR = function(){
var xhr = false;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}
else {
try {
xhr = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
}
return xhr;
}
// Check DOM whether is rendered.
var DOMReady = function(fn){
fn = typeof fn === "function" ? fn : function(){
};
(function(){
if (document && document.getElementsByTagName && document.getElementById && document.body) {
fn();
}
else {
setTimeout(arguments.callee, 10);
}
})();
}
//Execute the code from server.
var evalJS = function(text){
DOMReady(function(){
eval(text);
});
}
var InsertElement = {
"js": function(text){
var scripts = document.getElementsByTagName("script");
var script = scripts[scripts.length - 1];
script.text += text;
evalJS(text);
},
"javascript": this["js"],
"css": function(text){
var style = document.createElement("style");
style.setAttribute("type", "text/css");
style.innerHTML = text;
document.getElementsByTagName("head")[0].appendChild(style);
}
}
var combineJS = function(urls, path, type, split, compress){
split = split || ",";
type = type || "js";
var url = path + "?jspath=" + urls.join(split) + "&split=" + split;
var xhr = XHR();
xhr.open("GET", url, true);
xhr.onreadystatechange = function(){
if (xhr.readyState === 4 && xhr.status === 200) {
InsertElement[type](xhr.responseText);
}
}
xhr.send(null);
}
global.combineJS = combineJS;
})(window);
combineJS(["test1.js", "test2.js", "test3.js"], "combine.php", "js");
經(jīng)過測試,上面的方式跟第一種是一樣的效果,不理想。雖然減少了HTTP的請求數(shù)目,但是增加了服務(wù)器響應(yīng)的等待時間。
第三:仿照口碑網(wǎng)的實(shí)現(xiàn)方式,通過在服務(wù)端動態(tài)生成一個合并了幾個javascript或者CSS的文件,之后把整合后的文件的url返回給客戶端,之后進(jìn)行引用。《測試用例》
<?php
//file path param
$path=$_GET["jspath"];
//$path="/test/test/test1.css,/test/test/test2.css,/test/test/test3.css";
//split param
$split=$_GET["split"];
//$split=",";
//file path array
$path_arr=split($split,$path);
//store all files text
$js_str="";
//the count of file path array
$count=count($path_arr);
//read each file
for($i=0;$i<$count;$i++){
$file_read=fopen("http://www.ilovejs.net/lab/combine/".$path_arr[$i],"r");
while(!feof($file_read)){
$line=fgets($file_read);
if(strlen($line) ===1){
$js_str=$js_str.$line."\n";
}else{
$js_str=$js_str.$line;
}
}
fclose($file_read);
}
//write content to a file.
$write_file=fopen("http://www.ilovejs.net/lab/combine/".$path,"w+");
fwrite($write_file,$js_str);
fclose($write_file);
// response to client.
echo($path);
?>
之后客戶端通過resonseText獲得這個url,動態(tài)插入一個script或者link元素到head中。測試之后,上面的問題還是沒有解決。
總結(jié)下:上面的實(shí)現(xiàn)方式都是存在問題的:通過客戶端發(fā)送urls,服務(wù)端再整合文件,之后發(fā)送url或者內(nèi)容。這已經(jīng)存在了兩個必須的HTTP請求。這個就存在許多客觀的因素來影響這兩個HTTP請求的反應(yīng)時間:一個是網(wǎng)速,一個是客戶端和服務(wù)端操作系統(tǒng)的性能。
所以,比較合理的做法是通過參數(shù),在服務(wù)端直接整合成一個文件,生成之后客戶端再通過url鏈入到head中,這中間就需要一個跟普通通過script或者link鏈入文件的開銷,卻減少了許多個HTTP請求的開銷,這才能起到優(yōu)化的目的。下面是口碑網(wǎng)的最佳實(shí)踐方式:
<jsp:include page=”/CMS/headers/wrap/header.jsp”>
<jsp:param name=”css” value=”<%=STYLE_BASE_4_X_X+”,”+STYLE_HEAD+”,”+STYLE_XXX%>”/>
<jsp:param name=”charset” value=”utf-8″ />
</jsp:include>
通過上面參數(shù)的設(shè)置,事先在服務(wù)端生成這個整合的文件,之后頁面內(nèi)就通過script或者link來鏈入這個文件。
推薦文章
2025-01-18
2024-11-28
2024-11-09
2024-10-25
2024-06-25
2024-01-04
2023-11-06
2023-10-30
2023-10-13
2023-10-10
穩(wěn)定
產(chǎn)品高可用性高并發(fā)貼心
項目群及時溝通專業(yè)
產(chǎn)品經(jīng)理1v1支持快速
MVP模式小步快跑承諾
我們選擇聲譽(yù)堅持
10年專注高端品質(zhì)開發(fā)聯(lián)系我們
友情鏈接: