使用Minify來優化網站性能
Minify 是用PHP5開發的應用,通過遵循一些Yahoo的優化規則來提高網站的性能。它會合并多個CSS或者JavaScript文件,移除一些不必要的空格和 注釋,進行gzip壓縮,并且會設置瀏覽器的緩存頭。Minify 在設計上和Yahoo的 Combo Handler Service非常像,不過Minify可以合并任何你想要合并的JavaScript和CSS文件。
一般情況下,網站速度的瓶頸都在前端,而最關鍵的就是資源的加載速度,但是大多數瀏覽器都有單個域名并發請求數限制,所以如果一個頁面中存在很多的 資源,比如CSS和JavaScript文件,那么明顯會降低網站的加載速度,比較好處理方式就是把多個文件通過一個請求來訪問,這樣既不會影響之前的文 件維護,又會減少資源的清楚數量,Minify就是為之而生。下面是一些被Minify采用的 Yahoo! 優化準則:
2、Add an Expires or a Cache-Control Header
下面兩幅圖分別是啟用Minify之前和啟用Minify之后網站請求時間的一個對比,可以看出啟用Minify之后,資源的加載時間從250ms減少到了125ms,總共節省了50%的時間。
特性:
合并多個CSS或JavaScript文件為一個文件,減少請求數量,并且進行minify處理
使用了多個開源的庫,包括 JSMin.php ,Minify CSS,Minify HTML
服務端緩存(fils/APC/Memcache),可以避免不必要的重復處理
當瀏覽器存在資源的緩存,返回HTTP 304 Not Modified
多個文件合并時,自動生成URI
當開啟服務端緩存的時候,在一般的服務器上Minify每秒可以處理幾百個并發請求
根據請求頭,開啟Content-Encoding: gzip。在服務端緩存開啟的情況下,Minify提供gzipped 文件速度比Apache’s mod_deflate模塊要快
安裝
下載最新的Minify,然后解壓文件到”min” 文件夾
Copy “min” 文件夾到自己網站的DOCUMENT_ROOT下,如果想要Minify在子目錄下工作,看這里
使用
假設網站域名是http://example.com,Minify安裝在了虛擬主機的根目錄下,那么訪問http://example.com/min/,我們會看到一個“Minify URI Builder”,我們可以輸入需要進行合并的文件URI,如下
點擊Update之后,系統會自動生成一個合并后的URI:/min/?b=js&f=jquery-a.js,jquery- b.js,jquery-c.js。如果覺得URI太長,可以通過配置文件來指定group,編輯min目錄下的groupsConfig.php文件, 加入以下內容:
return array( 'js' => array('//js/jquery-a.js', '//js/jquery-b.js', '//js/jquery-c.js'),//相對于DOCUMENT_ROOT ); |
之后就可以通過/min/?g=js來訪問了
Minify在資源首次被請求的時候,會對多個文件進行合并,gzip,去除空格,注釋等處理,然后會把處理的結果進行緩存,默認情況下是進行文件緩存,緩存的key以minify_開頭,修改min/config.php文件,配置緩存文件存放的位置:
$min_cachePath = '/tmp';
除了通過文件進行緩存之外,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調試PHP錯誤,修改min/config.php文件,加入以下代碼:
$min_errorLogger = true; |
另一種是通過在URL中加入flag進行錯誤調試,在min/config.php中加入
$min_allowDebugFlag = true; |
之后就能以http://example.com/min/f=jquery-a.js,jquery-b.js,jquery-c.js&debug=1方式進行調試了
參考:
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.
設置/min/config.php文件 ,$min_cachePath 有3個選擇。
//$min_cachePath = ‘c:\\WINDOWS\\Temp’;
//$min_cachePath = ‘/tmp’;
//$min_cachePath = preg_replace(‘/^\\d+;/’, ”, session_save_path());
選擇第2個,去除// .設置tmp屬性777
在顯示的界面中加入你想合并壓縮的 js/css 路徑,點擊 ‘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請求,性能上確實能提高不少,特別是對于訪問量大的頁面。但是之前一直在思索兩個問題:
1.文件名中包含不規則的字符,在windows操作系統下是不允許作為文件名的。
2.瀏覽器的緩存怎么處理。
通過一些時間摸索和 跟同事進行探討,迷霧漸漸清晰了。對于一個問題,服務器端是進行了urlrewrite了,比如上面圖示的url鏈接其實是障眼法,是給別人看是這么個東 東(它包含了一些版本和文件名的信息,使用版本號的一個好處就是可以在版本升級的時候可以及時更新緩存,保持更行的css文件的及時生效),其實是服務器 端會將它rewrite到另外一個文件里。這個文件是會自動通過參數整合三個css文件到一個css文件里,最后download到客戶端。那么對于第二 個問題也就好解釋了,url不變,瀏覽器就會從緩存里讀取。
問題想清楚了,偶也就開始思索著自己去怎么實現這么個功能,下面是我摸索過程中所想到的辦法(經測試,都不太理想):
第一:通過服務端的一個程序文件(這里是PHP的一個小程序)來獲取客戶端發送的幾個javascript或者CSS的URL信息,之后通 過PHP腳本將這幾個文件整合為一個文件并把整合后的文件發送到客戶端,在客戶端再通過responseText把代碼動態生成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");
上面的方式是通過動態生成script或者style的方式來達到合并文件,減少HTTP請求的目的。但是測試中發現一個問題:客戶端請求合并后的 文件會等待比較長的時間,這跟直接鏈入幾個文件比起來,消耗的時間差距比較大。如下圖示顯示的合并后的文件插入到了文檔中,以及一直處于等待狀態的一個問 題(1s~6s之間)。《測試用例》
第二:合并幾個文件的方法和上面的一樣,只是我將合并后的文件內容返回來的時候插入到當前發送請求的script標簽內,這得益于一個技 巧:在加載的時候,當前的script元素永遠是處于scripts.length-1的索引位置。因此可以在js代碼里面進行改寫:《測試用例》
(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");
經過測試,上面的方式跟第一種是一樣的效果,不理想。雖然減少了HTTP的請求數目,但是增加了服務器響應的等待時間。
第三:仿照口碑網的實現方式,通過在服務端動態生成一個合并了幾個javascript或者CSS的文件,之后把整合后的文件的url返回給客戶端,之后進行引用。《測試用例》
<?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,動態插入一個script或者link元素到head中。測試之后,上面的問題還是沒有解決。
總結下:上面的實現方式都是存在問題的:通過客戶端發送urls,服務端再整合文件,之后發送url或者內容。這已經存在了兩個必須的HTTP請求。這個就存在許多客觀的因素來影響這兩個HTTP請求的反應時間:一個是網速,一個是客戶端和服務端操作系統的性能。
所以,比較合理的做法是通過參數,在服務端直接整合成一個文件,生成之后客戶端再通過url鏈入到head中,這中間就需要一個跟普通通過script或者link鏈入文件的開銷,卻減少了許多個HTTP請求的開銷,這才能起到優化的目的。下面是口碑網的最佳實踐方式:
<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>
通過上面參數的設置,事先在服務端生成這個整合的文件,之后頁面內就通過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
穩定
產品高可用性高并發貼心
項目群及時溝通專業
產品經理1v1支持快速
MVP模式小步快跑承諾
我們選擇聲譽堅持
10年專注高端品質開發