在服务端合并和压缩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的《前端性能优化和自动化》中也介绍了它。

您或许有兴趣:

标签:, , ,

作者:秦歌,时间:2010-06-08 1:50,归纳于:Apache & MySQL & PHP, Javascript & DOM & AJAX,订阅:RSS 2.0,引用:Trackback

有评论 55 条,发表一条新评论 »

邦彦说:2010-06-10 21:11 #1

回头试一下Minify的配置,不知道像K2这样模块越来越细分的做法,Minify对缓存造成的影响会有多大。

则名说:2010-06-11 1:15 #2

恩,哈。正准备这样搞

在服务端合并和压缩JavaScript和CSS文件 « 可乐加糖说:2010-06-11 9:05 #3

[...] 转载地址:http://dancewithnet.com/2010/06/08/minify-js-and-css-files-in-server/ [...]

Ysd说:2010-06-11 10:04 #4

这样的话JS文件基本都没有缓存了吧?

Guest说:2010-06-11 20:15 #5

弱弱的问下这个的授权情况,是否可以用于商业用途?

秦歌说:2010-06-11 22:31 #6

@Ysd :服务端合并就相当于自动生成一个新的js或者css文件,其缓存情况和普通的js文件完全一致。
@Guest : Minify采用的是New BSD License,非常的自由,可以使用在商业应用或者专有软件上,我会在文中补充一下。

在服务端合并和压缩JavaScript和CSS文件说:2010-06-12 9:52 #7

[...] 转载地址:http://dancewithnet.com/2010/06/08/minify-js-and-css-files-in-server/ [...]

Guest说:2010-06-12 10:20 #8

@秦歌:谢谢秦歌兄弟的回复。
不知道是我理解错了还是啥,好像你使用了一些BSD License的源程序,你自己发布程序也需要变成BSD License的,而团队这边的程序是收费的,有冲突吗?感谢告知!

阿熊说:2010-06-12 14:59 #9

呵呵,偶是包子,我们现在的新代码中有用到minify做压缩,不过是在主应用代码中做html/css/js压缩,combo的服务的确好,但我测试过超过一定数据量的数据合并就会出问题,php实时计算的时间超时就会导致这个请求挂掉,css和html出现的情况不大可能,js出现的可能性比较大,所以静态请求合并以及min的事情都是在发布前由shell 先做cat合并然后用YUI Compressor压缩搞定,这部分以后会做自动化,我想这也是前端的一种趋势,脚本语言变为“类编译”型语言。正式运行前都有个“编译”优化的流程。
有空来南京玩,我请客,哈哈~~

Ysd说:2010-06-13 13:33 #10

@​​秦​​歌​​ 可能是我没看明白~你的意思是在服务器第一次合并完直接生成一个文件,以后访问直接访问这个文件?而不是每次访问的时候生成?

evan说:2010-06-13 19:49 #11

@包子 我(有脚)也在开发机上这样玩,写个 sh 在 crontab 里跑 svn up 检测版本是否有更新再 cat arg > min.js|css 然后再 yui-compressor。用 php 做 combo server 总觉得不安稳,不如部署一台发布服务器再提交到 CDN 上更靠谱

秦歌说:2010-06-13 21:35 #12

@Guest:你理解错了,这个基本上可以自由使用的,不需要你的程序也用BSD协议的,但是如果你的程序使用了BSD协议的源码,需要在引用的原源码中保留BSD协议,二进制的需要在版权声明中包含源码协议而已。
@阿熊:到南京一定要找你哈,我在应用中暂时还没有遇到PHP应用的瓶颈,毕竟只有第一次才需要PHP。每次发布前合并还是不够灵活的,前端应用需要服务端的实时合并。不过,淘宝叔度说淘宝准备开源nginx模块上的js和css合并模块,这非常值得期待,将非常高效。
@Ysd:是的,你的理解是对的。

阿熊说:2010-06-14 8:39 #13

@秦歌 在我看来实时合并有失败的风险,我有一个合并前共500多k的js,合并压缩后文件200多k,实时合并会失败,但命令行压缩没有超时这一说。
既然是一次运行多次使用的方式,更建议编译方式搞,类似于java中的class概念。此外解决我说的这种问题也可以使用异步调用命令行子进程的方式解决,不一定要依赖在fcgi当前进程内解决问题。

大雾说:2010-06-17 15:14 #14

我之前研究的那个已经在项目中用了。用的是csstidy,jsmin进行css和js的压缩优化处理的。第一次访问时按配置策略进行合并压缩发布成一个文件,第二次访问时直接访问生成的文件。配置文件时每个页面一个tag标记,根据tag标记到配置找到所需的子文件资源,然后合并压缩。

用这种机制,主要看文件的颗粒度模块怎么分。
达到http请求的优化只是策略用途的一个方面,

更大的应用和需要研究的地方,是就css,js开发的特点来研究如何进行模块的划分,并进行协作开发。

如何在项目中规划css的模块化开发,以及通过csstidy这种类似的开源提供的一些功能进行css开发协作的构思。
比如一份文件 #someid {结构属性集;表象属性集;} 可以将该样式拆分成两种样式文件,
一种只负责页面的结构呈现: #someid{结构属性集;}
另一种只负责页面的表象呈现: #someid {表象属性集;}
开发时,可以分开来写,而通过csstidy以及定义的发布压缩的机制,最终可以还原压缩成 #someid {结构属性集;表象属性集;}
这种结构样式,和表象样式可以协同来开发。

这次应用到最后,我文件颗粒度分的比较细,但还不是那么合理。

“comm1″:[ "comm/reset.css",
"comm/para.css",
"comm/define.css",
"comm/box.css",
"comm/tab.css",
"comm/form.css",
"comm/table.css",
"comm/pager.css",
"comm/layout.css",
"comm/sprite.css"
],
这是我配置里面定义的一个公有模块样式组。

classictallboots说:2010-07-02 23:02 #15

感觉很深奥~~没看懂,果然术业有专攻。只会ASP,落后了

[zt]在服务端合并和压缩 JavaScript和CSS文件 « interUI说:2010-07-03 1:20 #16

[...] 作者:秦歌,时间:2010-06-08 1:50,归纳于:Apache & MySQL & PHP, Javascript & DOM & AJAX,订阅:RSS 2.0,引用:Trackback [...]

[zt]在服务端合并和压缩 JavaScript和CSS文件 « interUI说:2010-07-03 1:22 #17

[...] 在 服务端合并和压缩 JavaScript和CSS文件 [...]

摇滚肉包子说:2010-07-03 17:50 #18

minify这个工具应该说相当的值得称道。我们目前的网站http://www.hxfang.com,从建立网站之初就一直采用minify来做压缩,目前为止没有碰到过什么问题。效果相当好。

它带来的很大的好处就是修改了文件后不需要手动去压缩,然后再上传。这点真的是开发者的福音哪。

狂热说:2010-07-06 23:21 #19

我跟着修改了,不知道是什么问题,总是有出错。。

有光说:2010-07-21 15:00 #20

ie有什么比较好的调试工具吗 我的博客ie6下问题不小 可是只能装一个版本的IE

游客说:2010-07-26 20:51 #21

很不错。。。呵呵。。。继续努力。。

threecups说:2010-07-28 12:58 #22

不错,学习了。 目前采用的是YUI的Minify

寒光说:2010-08-04 15:50 #23

谢谢

Sunflowamedia说:2010-08-13 11:15 #24

这些东西暂时不会被使用,太高端了。

NFL Jersey说:2010-08-23 13:59 #25

怎么试了,老是显示有错误呢

int说:2010-09-16 15:17 #26

不错呢

寒霜说:2010-10-11 17:42 #27

http://k.kbcdn.com/min/builder/怎么感觉这么个地址暴露了很危险

秦歌说:2010-10-13 23:56 #28

是有可能存在的风险,SA同学有点小疏忽,已经封掉,多谢。

songmuyi说:2010-10-26 10:30 #29

呵呵,好久不见

寒霜说:2010-10-26 15:06 #30

看了下口碑的首页的Yslow得分是A,果然厉害!

freetao说:2010-10-26 18:36 #31

额,目前都没接触到的技术。

寒霜说:2010-11-23 18:55 #32

我最近发现一个问题,使用minify后速度竟然比未使用前更慢,使用后竟然延长了响应时间!请问秦歌有没有发生过这种状况,还是我哪里没调试好,或者没安装好!

秦歌说:2010-12-04 9:13 #33

@韩霜:你是否开发了minify本身提供的缓存设置,或者你可以提供其他缓存,minify只是提供一个合并工具而已,如果没有对静态文件进行缓存,那肯定速度会慢很多的。

寒霜说:2010-12-06 13:29 #34

@秦歌:开启了缓冲,没合并压缩前浏览器的并行下载获取文件的时间还是很客观的,但是用了minify后firebug中显示的下载时间时间上是有点改观,但是响应时间太长了!

char1es_wu说:2010-12-14 10:56 #35

http://localhost/min/f=yui/3.2.0/widget/assets/skins/sam/widget.css,yui/3.2.0/console/assets/skins/sam/bg.png

按你的例子做了一个,结果出现了上面的链接。。。很困惑。。。。

app ui说:2010-12-27 11:06 #36

有空试下

匿名说:2011-03-07 12:28 #37

您写的非常好,虚心学习

idamag说:2011-07-11 14:43 #38

又学习了~

devil说:2011-07-25 14:56 #39

java 里面有跟 nimify 一样类似使用的东西么?

在服务端合并和压缩JavaScript和CSS文件 | 狼的blog说:2011-11-28 21:02 #40

[...] 在服务端合并和压缩JavaScript和CSS文件 This entry was posted in 前端技术. Bookmark the permalink. ← 虎扑网架构升级之路 [...]

sking7说:2011-12-15 12:54 #41

minify。。呵呵。。学习了。。

sea说:2011-12-25 14:22 #42

真不错,学习。

HanChn说:2012-02-24 16:57 #43

请问有这方面的相关教程吗?

gardencn说:2012-03-26 8:10 #44

我们的网站www.nbhuntop.com也通过minify优化过了 效果还是显而易见的 加载时间提升不少
prototype.js
scriptaculous.js?load=effects
lightbox.js
的进一步合并问题 仍然没解决 恳请松哥点拨一下 谢谢!

服务器端自动合并JS文件(1)—php下的minify at Putaoshu's blog说:2012-07-05 18:25 #45

[...] 1.minify homepage 2.在服务端合并和压缩JavaScript和CSS文件 @随网之舞 3.启用Mod Rewrite和.htaccess @随网之舞 更多 [...]

LiZn说:2012-09-12 23:08 #46

跟着文章的设置,最近用YUI3.6试了下,很容易就可以启用combo,就是YUI无视了我loaderPath的配置,只好去修改yui-min.js……看了文档,loaderPath还没有标识为deprecated,不知道什么情况配置不起作用

jielee说:2012-09-25 12:12 #47

使用minify是会生成这个文件还是通过请求再从服务端合并呢?如果是后者 那我就不清楚如何在CND使用了

在服务端合并和压缩JavaScript和CSS文件 | 文章精读说:2012-10-29 10:53 #48

[...] 通过在服务端合并和压缩JavaScript和CSS文件 @ 随网之舞. [...]

Drumer_Coder说:2012-11-29 9:34 #49

这个我都尝试了,为什么都不好用。自己搭建apache+php那个总报400!
菜鸟求教!

哀音说:2012-12-16 17:16 #50

YUI 3.8里找到了
comboSep属性,可以不用修改js了。
http://yuilibrary.com/yui/docs/api/classes/Loader.html#property_comboSep

Troland说:2012-12-28 14:18 #51

挺不错的。初学者很多还是不太明白。

关于js压缩 - 开发者问答说:2013-04-25 8:23 #52

[...] 回答: 分享一份YUI Combo Handler资料在服务端合并和压缩JavaScript和CSS文件 回答: jquery源码就有两个包呀,一个是带有-min的 回答: 引用 2 楼 [...]

【已解决】一个网站中,如何定义CSS 框架结构? - 开发者问答说:2013-04-25 13:52 #53

[...] http://dancewithnet.com/2010/06/ … e-1/#comment-234656 [...]

【已解决】一个网站中,如何定义CSS 框架结构? - 开发者问答说:2013-04-25 13:52 #54

[...] http://dancewithnet.com/2010/06/ … e-1/#comment-234656 [...]

大家写完css都进行压缩和优化吗 - web前端开发 - 开发者问答说:2013-05-03 4:48 #55

[...] 回复: 可以使用minify,minify是一个php网站程序,它可以自动拼接压缩JS、CSS,去除注释,还可以启用Gzip,生成缓存文件返回给浏览器。开发的JS、CSS不用压缩,直接上传到minify站点下,灰常的方便啦 调用方式:http://minify.xxx.com/min/?f=/css/base.css,/css/1rs.css,JS相同 具体的使用方法,大家可以去网上搜一下,这里列出一个 http://dancewithnet.com/2010/06/ … ss-files-in-server/ [...]

发表一条评论

您可以在下面评论内容中使用下列XHTML标签:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

您或许有兴趣:


回到页眉