使用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年专注高端品质开发