﻿<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Xixi&#039;s Blog &#187; gzip</title>
	<atom:link href="http://www.xixis.net/archives/tag/gzip/feed" rel="self" type="application/rss+xml" />
	<link>http://www.xixis.net</link>
	<description>http://www.xixis.net  &#124;  Eternal Sunshine of the Spotless Mind</description>
	<lastBuildDate>Thu, 12 Jan 2012 06:04:19 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>flickr对javascript干的好事</title>
		<link>http://www.xixis.net/archives/flickr-doing-a-good-thing-for-javascript.html</link>
		<comments>http://www.xixis.net/archives/flickr-doing-a-good-thing-for-javascript.html#comments</comments>
		<pubDate>Sun, 07 Feb 2010 15:17:43 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[gzip]]></category>

		<guid isPermaLink="false">http://www.xixis.net/?p=308</guid>
		<description><![CDATA[在一个讨论web技术的网站vitamin上发现这篇《Serving JavaScript Fast》，读过之后大有收获，茅塞顿开。于是就有了翻译过来的念头——我这人有个毛病，看到有意思的英文文章，就想自己翻过来（虽然英文水平很烂）。先在网上查了查，已经有blog谈到这篇文章（我算是后知后觉了），有总结要点的《Flickr 的开发者的 Web 应用优化技巧》，也有延伸开来的《接着讲Flickr的八卦》，但似乎没有全文翻译的（这下就好，不会忙了半天发现是无用功）。之后，就写信问作者可不可以，作者一口答应：“sure &#8211; i’d love you to translate it”，只是要求我翻好之后给他一个链接地址。得到准许，心里就有底了。
 

先介绍一下作者。Cal Henderson，伦敦人，现居加利福尼亚的旧金山。PHP，MySQL和Perl专家，现任flickr架构师（flickr被收购后就在yahoo了），同时也是vitamin的特聘顾问（写些技术性文章）。
既然他是架构师，flickr用的应该就是文中谈到的这些技术，于是参照文章，再对比网站，种种迹象表明确实如此。虽然在中国访问flickr速度不敢恭维，加速效果不得而知，但其用了n多css和javascript资源却似乎从没出过什么问题，也从侧面印证了这些技术的有效性。
仔细的看完文章，还有个强烈的感觉：这老兄也太能卖关子了，一句话非分成三句说，摆事实讲道理是够透彻，就是有点太@#$%了…… 算了，他怎么说我怎么翻吧，忠实于原著嘛，要不就成篡改了。经过几天努力，加上同事thincat兄倾力援手（小弟不胜感激啊），终于完工（@_@ 真是苦力活啊，我再也不想干了～）。
全文翻译如下：
让javascript跑得更快
作者：Cal Henderson
下一代web应用让javascript和css得堪大用。我们会告诉你怎样使这些应用又快又灵。
建立了号称“Web 2.0”的应用，也实现了富内容（rich content）和交互，我们期待着css和javascript扮演更加重要的角色。为使应用干净利落，我们需要完善那些渲染页面的文件，优化其大小和形态，以确保提供最好的用户体验——在实践中，这就意味着一种结合：使内容尽可能小、下载尽可能快，同时避免对未改动资源不必要的重新获取。
由于css和js文件的形态，情况有点复杂。跟图片相比，其源代码很有可能频繁改动。而一旦改动，就需要客户端重新下载，使本地缓存无效（保存在其他缓存里的版本也是如此）。在这篇文章里，我们将着重探讨怎样使用户体验最快：包括初始页面的下载，随后页面的下载，以及随着应用渐进、内容变化而进行的资源下载。
我始终坚信这一点：对开发者来说，应该尽可能让事情变得简单。所以我们青睐于那些能让系统自动处理优化难题的方法。只需少许工作量，我们就能建立一举多得的环境：它使开发变得简单，有极佳的终端性能，也不会改变现有的工作方式。
好大一沱
老的思路是，为优化性能，可以把多个css和js文件合并成极少数大文件。跟十个5k的js文件相比，合并成一个50k的文件更好。虽然代码总字节数没变，却避免了多个HTTP请求造成的开销。每个请求都会在客户端和服务器两边有个建立和消除的过程，导致请求和响应header带来开销，还有服务器端更多的进程和线程资源消耗（可能还有为压缩内容耗费的cpu时间）。
（除了HTTP请求，）并发问题也很重要。默认情况下，在使用持久连接（persistent connections）时，ie和firefox在同一域名内只会同时下载两个资源（在HTTP 1.1规格书中第8.1.4节的建议）（htmlor注：可以通过修改注册表等方法改变这一默认配置）。这就意味着，在我们等待下载2个js文件的同时，将无法下载图片资源。也就是说，这段时间内用户在页面上看不到图片。
（虽然合并文件能解决以上两个问题，）可是，这个方法有两个缺点。第一，把所有资源一起打包，将强制用户一次下载完所有资源。如果（不这么做，而是）把大块内容变成多个文件，下载开销就分散到了多个页面，同时缓解了会话中的速度压力（或完全避免了某些开销，这取决于用户选择的路径）。如果为了随后页面下载得更快而让初始页面下载得很慢，我们将发现更多用户根本不会傻等着再去打开下一个页面。
第二（这个影响更大，一直以来却没怎么被考虑过），在一个文件改动很频繁的环境里，如果采用单文件系统，那么每次改动文件都需要客户端把所有css和js重新下载一遍。假如我们的应用有个100k的合成的js大文件，任何微小的改动都将强制客户端把这100k再消化一遍。
分解之道
（看来合并成大文件不太合适。）替代方案是个折中的办法：把css和js资源分散成多个子文件，按功能划分、保持文件个数尽可能少。这个方案也是有代价的，虽说开发时代码分散成逻辑块（logical chunks）能提高效率，可在下载时为提高性能还得合并文件。不过，只要给build系统（把开发代码变成产品代码的工具集，是为部署准备的）加点东西，就没什么问题了。
对于有着不同开发和产品环境的应用来说，用些简单的技术可以让代码更好管理。在开发环境下，为使条理清晰，代码可以分散为多个逻辑部分（logical components）。可以在Smarty（一种php模板语言）里建立一个简单的函数来管理javascript的下载：


SMARTY:
&#123;insert_js files=&#34;foo.js,bar.js,baz.js&#34;&#125;
&#160;
PHP:
function smarty_insert_js&#40;$args&#41;&#123;   foreach &#40;explode&#40;',', $args&#91;'files'&#93;&#41; as $file&#41;&#123;     echo &#34;&#60;script type=\&#34;text/javascript\&#34; SOURCE=\&#34;/javascript/$file\&#34;&#62;&#60;/script&#62;\\n&#34;;   &#125;
&#125;
&#160;
OUTPUT:
&#60;script type=&#34;text/javascript&#34; SOURCE=&#34;/javascript/foo.js&#34;&#62;&#60;/script&#62;
&#60;script type=&#34;text/javascript&#34; SOURCE=&#34;/javascript/bar.js&#34;&#62;&#60;/script&#62;
&#60;script type=&#34;text/javascript&#34; SOURCE=&#34;/javascript/baz.js&#34;&#62;&#60;/script&#62;

（htmlor注：wordpress中会把“src”替换成不知所谓的字符，因此这里只有写成“SOURCE”，使用代码时请注意替换，下同）
就这么简单。然后我们就命令build过程（build process）去把确定的文件合并起来。这个例子里，合并的是foo.js和bar.js，因为它们几乎总是一起下载。我们能让应用配置记住这一点，并修改模板函数去使用它。（代码如下：）

SMARTY:
&#123;insert_js files=&#34;foo.js,bar.js,baz.js&#34;&#125;
&#160;
PHP:
# 源文件映射图。在build过程合并文件之后用这个图找到js的源文件。

$GLOBALS&#91;'config'&#93;&#91;'js_source_map'&#93; = array&#40;   'foo.js'	=&#62; 'foobar.js',   'bar.js'	=&#62; 'foobar.js',   'baz.js'	=&#62; 'baz.js',
&#41;;
&#160;
function smarty_insert_js&#40;$args&#41;&#123;   if &#40;$GLOBALS&#91;'config'&#93;&#91;'is_dev_site'&#93;&#41;&#123;     $files = explode&#40;',', $args&#91;'files'&#93;&#41;;   &#125;else&#123;     $files = array&#40;&#41;; ...]]></description>
			<content:encoded><![CDATA[<p>在一个讨论web技术的网站<a href="http://www.thinkvitamin.com/">vitamin</a>上发现这篇<a href="http://www.thinkvitamin.com/features/webapps/serving-javascript-fast">《Serving JavaScript Fast》</a>，读过之后大有收获，茅塞顿开。于是就有了翻译过来的念头——我这人有个毛病，看到有意思的英文文章，就想自己翻过来（虽然英文水平很烂）。先在网上查了查，已经有blog谈到这篇文章（我算是后知后觉了），有总结要点的<a href="http://www.dbanotes.net/web/flickr_web_tech.html">《Flickr 的开发者的 Web 应用优化技巧》</a>，也有延伸开来的<a href="http://blog.94smart.com/2006/05/26/744.html">《接着讲Flickr的八卦》</a>，但似乎没有全文翻译的（这下就好，不会忙了半天发现是无用功）。之后，就写信问作者可不可以，作者一口答应：“sure &#8211; i’d love you to translate it”，只是要求我翻好之后给他一个链接地址。得到准许，心里就有底了。</p>
<p> <span id="more-308"></span>
</p>
<p>先介绍一下作者。<a href="http://www.iamcal.com/">Cal Henderson</a>，伦敦人，现居加利福尼亚的旧金山。PHP，MySQL和Perl专家，现任<a href="http://www.flickr.com/">flickr</a>架构师（flickr被收购后就在<a href="http://www.yahoo.com/">yahoo</a>了），同时也是vitamin的<a href="http://www.thinkvitamin.com/advisors/cal_henderson.php">特聘顾问</a>（写些技术性文章）。</p>
<p>既然他是架构师，flickr用的应该就是文中谈到的这些技术，于是参照文章，再对比网站，种种迹象表明确实如此。虽然在中国访问flickr速度不敢恭维，加速效果不得而知，但其用了n多css和<span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/javascript" title="显示javascript的所有日志" target="_blank">javascript</a></span>资源却似乎从没出过什么问题，也从侧面印证了这些技术的有效性。</p>
<p>仔细的看完文章，还有个强烈的感觉：这老兄也太能卖关子了，一句话非分成三句说，摆事实讲道理是够透彻，就是有点太@#$%了…… 算了，他怎么说我怎么翻吧，忠实于原著嘛，要不就成篡改了。经过几天努力，加上同事<a href="http://my.donews.com/thincat/">thincat</a>兄倾力援手（小弟不胜感激啊），终于完工（@_@ 真是苦力活啊，我再也不想干了～）。</p>
<p>全文翻译如下：</p>
<h4>让<span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/javascript" title="显示javascript的所有日志" target="_blank">javascript</a></span>跑得更快</h4>
<p>作者：Cal Henderson</p>
<p>下一代web应用让javascript和css得堪大用。我们会告诉你怎样使这些应用又快又灵。</p>
<p>建立了号称“Web 2.0”的应用，也实现了富内容（rich content）和交互，我们期待着css和javascript扮演更加重要的角色。为使应用干净利落，我们需要完善那些渲染页面的文件，优化其大小和形态，以确保提供最好的用户体验——在实践中，这就意味着一种结合：使内容尽可能小、下载尽可能快，同时避免对未改动资源不必要的重新获取。</p>
<p>由于css和js文件的形态，情况有点复杂。跟图片相比，其源代码很有可能频繁改动。而一旦改动，就需要客户端重新下载，使本地缓存无效（保存在其他缓存里的版本也是如此）。在这篇文章里，我们将着重探讨怎样使用户体验最快：包括初始页面的下载，随后页面的下载，以及随着应用渐进、内容变化而进行的资源下载。</p>
<p>我始终坚信这一点：对开发者来说，应该尽可能让事情变得简单。所以我们青睐于那些能让系统自动处理优化难题的方法。只需少许工作量，我们就能建立一举多得的环境：它使开发变得简单，有极佳的终端性能，也不会改变现有的工作方式。</p>
<h3>好大一沱</h3>
<p>老的思路是，为优化性能，可以把多个css和js文件合并成极少数大文件。跟十个5k的js文件相比，合并成一个50k的文件更好。虽然代码总字节数没变，却避免了多个HTTP请求造成的开销。每个请求都会在客户端和服务器两边有个建立和消除的过程，导致请求和响应header带来开销，还有服务器端更多的进程和线程资源消耗（可能还有为压缩内容耗费的cpu时间）。</p>
<p>（除了HTTP请求，）并发问题也很重要。默认情况下，在使用持久连接（persistent connections）时，ie和firefox在同一域名内只会同时下载<a href="http://blogs.msdn.com/ie/archive/2005/04/11/407189.aspx">两个资源</a>（在<a href="http://www.ietf.org/rfc/rfc2616.txt">HTTP 1.1规格书</a>中第8.1.4节的建议）（htmlor注：可以通过修改注册表等方法改变这一默认配置）。这就意味着，在我们等待下载2个js文件的同时，将无法下载图片资源。也就是说，这段时间内用户在页面上看不到图片。</p>
<p>（虽然合并文件能解决以上两个问题，）可是，这个方法有两个缺点。第一，把所有资源一起打包，将强制用户一次下载完所有资源。如果（不这么做，而是）把大块内容变成多个文件，下载开销就分散到了多个页面，同时缓解了会话中的速度压力（或完全避免了某些开销，这取决于用户选择的路径）。如果为了随后页面下载得更快而让初始页面下载得很慢，我们将发现更多用户根本不会傻等着再去打开下一个页面。</p>
<p>第二（这个影响更大，一直以来却没怎么被考虑过），在一个文件改动很频繁的环境里，如果采用单文件系统，那么每次改动文件都需要客户端把所有css和js重新下载一遍。假如我们的应用有个100k的合成的js大文件，任何微小的改动都将强制客户端把这100k再消化一遍。</p>
<h3>分解之道</h3>
<p>（看来合并成大文件不太合适。）替代方案是个折中的办法：把css和js资源分散成多个子文件，按功能划分、保持文件个数尽可能少。这个方案也是有代价的，虽说开发时代码分散成逻辑块（logical chunks）能提高效率，可在下载时为提高性能还得合并文件。不过，只要给build系统（把开发代码变成产品代码的工具集，是为部署准备的）加点东西，就没什么问题了。</p>
<p>对于有着不同开发和产品环境的应用来说，用些简单的技术可以让代码更好管理。在开发环境下，为使条理清晰，代码可以分散为多个逻辑部分（logical components）。可以在<a href="http://smarty.php.net/">Smarty</a>（一种php模板语言）里建立一个简单的函数来管理javascript的下载：</p>
<blockquote>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">SMARTY<span style="color: #339933;">:</span>
<span style="color: #009900;">&#123;</span>insert_js files<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;foo.js,bar.js,baz.js&quot;</span><span style="color: #009900;">&#125;</span>
&nbsp;
PHP<span style="color: #339933;">:</span>
<span style="color: #000000; font-weight: bold;">function</span> smarty_insert_js<span style="color: #009900;">&#40;</span><span style="color: #000088;">$args</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>   <span style="color: #b1b100;">foreach</span> <span style="color: #009900;">&#40;</span><span style="color: #990000;">explode</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">','</span><span style="color: #339933;">,</span> <span style="color: #000088;">$args</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'files'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$file</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>     <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;&lt;script type=<span style="color: #000099; font-weight: bold;">\&quot;</span>text/javascript<span style="color: #000099; font-weight: bold;">\&quot;</span> SOURCE=<span style="color: #000099; font-weight: bold;">\&quot;</span>/javascript/<span style="color: #006699; font-weight: bold;">$file</span><span style="color: #000099; font-weight: bold;">\&quot;</span>&gt;&lt;/script&gt;<span style="color: #000099; font-weight: bold;">\\</span>n&quot;</span><span style="color: #339933;">;</span>   <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
OUTPUT<span style="color: #339933;">:</span>
<span style="color: #339933;">&lt;</span>script type<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;text/javascript&quot;</span> SOURCE<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;/javascript/foo.js&quot;</span><span style="color: #339933;">&gt;&lt;/</span>script<span style="color: #339933;">&gt;</span>
<span style="color: #339933;">&lt;</span>script type<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;text/javascript&quot;</span> SOURCE<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;/javascript/bar.js&quot;</span><span style="color: #339933;">&gt;&lt;/</span>script<span style="color: #339933;">&gt;</span>
<span style="color: #339933;">&lt;</span>script type<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;text/javascript&quot;</span> SOURCE<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;/javascript/baz.js&quot;</span><span style="color: #339933;">&gt;&lt;/</span>script<span style="color: #339933;">&gt;</span></pre></div></div>

<p>（htmlor注：wordpress中会把“src”替换成不知所谓的字符，因此这里只有写成“SOURCE”，使用代码时请注意替换，下同）</p>
<p>就这么简单。然后我们就命令build过程（build process）去把确定的文件合并起来。这个例子里，合并的是foo.js和bar.js，因为它们几乎总是一起下载。我们能让应用配置记住这一点，并修改模板函数去使用它。（代码如下：）</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">SMARTY<span style="color: #339933;">:</span>
<span style="color: #009900;">&#123;</span>insert_js files<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;foo.js,bar.js,baz.js&quot;</span><span style="color: #009900;">&#125;</span>
&nbsp;
PHP<span style="color: #339933;">:</span>
<span style="color: #666666; font-style: italic;"># 源文件映射图。在build过程合并文件之后用这个图找到js的源文件。
</span>
<span style="color: #000088;">$GLOBALS</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'config'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'js_source_map'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>   <span style="color: #0000ff;">'foo.js'</span>	<span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'foobar.js'</span><span style="color: #339933;">,</span>   <span style="color: #0000ff;">'bar.js'</span>	<span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'foobar.js'</span><span style="color: #339933;">,</span>   <span style="color: #0000ff;">'baz.js'</span>	<span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'baz.js'</span><span style="color: #339933;">,</span>
<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">function</span> smarty_insert_js<span style="color: #009900;">&#40;</span><span style="color: #000088;">$args</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>   <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$GLOBALS</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'config'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'is_dev_site'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>     <span style="color: #000088;">$files</span> <span style="color: #339933;">=</span> <span style="color: #990000;">explode</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">','</span><span style="color: #339933;">,</span> <span style="color: #000088;">$args</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'files'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>   <span style="color: #009900;">&#125;</span><span style="color: #b1b100;">else</span><span style="color: #009900;">&#123;</span>     <span style="color: #000088;">$files</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>     <span style="color: #b1b100;">foreach</span> <span style="color: #009900;">&#40;</span><span style="color: #990000;">explode</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">','</span><span style="color: #339933;">,</span> <span style="color: #000088;">$args</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'files'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$file</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>       <span style="color: #000088;">$files</span><span style="color: #009900;">&#91;</span><span style="color: #000088;">$GLOBALS</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'config'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'js_source_map'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #000088;">$file</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">++;</span>     <span style="color: #009900;">&#125;</span>     <span style="color: #000088;">$files</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array_keys</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$files</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>   <span style="color: #009900;">&#125;</span>   <span style="color: #b1b100;">foreach</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$files</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$file</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>     <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;&lt;script type=<span style="color: #000099; font-weight: bold;">\&quot;</span>text/javascript<span style="color: #000099; font-weight: bold;">\&quot;</span> SOURCE=<span style="color: #000099; font-weight: bold;">\&quot;</span>/javascript/<span style="color: #006699; font-weight: bold;">$file</span><span style="color: #000099; font-weight: bold;">\&quot;</span>&gt;&lt;/script&gt;<span style="color: #000099; font-weight: bold;">\\</span>n&quot;</span><span style="color: #339933;">;</span>   <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
OUTPUT<span style="color: #339933;">:</span>
<span style="color: #339933;">&lt;</span>script type<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;text/javascript&quot;</span> SOURCE<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;/javascript/foobar.js&quot;</span><span style="color: #339933;">&gt;&lt;/</span>script<span style="color: #339933;">&gt;</span>
<span style="color: #339933;">&lt;</span>script type<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;text/javascript&quot;</span> SOURCE<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;/javascript/baz.js&quot;</span><span style="color: #339933;">&gt;&lt;/</span>script<span style="color: #339933;">&gt;</span></pre></div></div>

</blockquote>
<p>模板里的源代码没必要为了分别适应开发和产品阶段而改动，它帮助我们在开发时保持文件分散，发布成产品时把文件合并。想更进一步的话，可以把合并过程（merge process）写在php里，然后使用同一个（合并文件的）配置去执行。这样就只有一个配置文件，避免了同步问题。为了做的更加完美，我们还可以分析css和js文件在页面中同时出现的几率，以此决定合并哪些文件最合理（几乎总是同时出现的文件是合并的首选）。</p>
<p>对css来说，可以先建立一个主从关系的模型，它很有用。一个主样式表控制应用的所有样式表，多个子样式表控制不同的应用区域。采用这个方法，大多数页面只需下载两个css文件，而其中一个（指主样式表）在页面第一次请求时就会缓存。</p>
<p>对没有太多css和js资源的应用来说，这个方法在第一次请求时可能比单个大文件慢，但如果保持文件数量很少的话，你会发现其实它更快，因为每个页面的数据量更小。让人头疼的下载花销被分散到不同的应用区域，因此并发下载数保持在一个最小值，同时也使得页面的平均下载数据量很小。</p>
<h3>压缩</h3>
<p>谈到资源压缩，大多数人马上会想到<a href="http://sourceforge.net/projects/mod-gzip/">mod_gzip</a>（但要当心，mod_gzip实际上是个魔鬼，至少能让人做恶梦）。它的原理很简单：浏览器请求资源时，会发送一个header表明自己能接受的内容编码。就像这样：</p>
<blockquote>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">Accept<span style="color: #339933;">-</span>Encoding<span style="color: #339933;">:</span> <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/gzip" title="显示gzip的所有日志" target="_blank">gzip</a></span><span style="color: #339933;">,</span>deflate</pre></div></div>

</blockquote>
<p>服务器遇到这样的header请求时，就用<span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/gzip" title="显示gzip的所有日志" target="_blank">gzip</a></span>或deflate压缩内容发往客户端，然后客户端解压缩。这过程减少了数据传输量，同时消耗了客户端和服务器的cpu时间。也算差强人意。但是，mod_gzip的工作方式是这样的：先在磁盘上创建一个临时文件，然后发送（给客户端），最后删除这个文件。在高容量的系统中，由于磁盘io问题，很快就会达到极限。要避免这种情况，可以改用<a href="http://httpd.apache.org/docs/2.0/mod/mod_deflate.html">mod_deflate</a>（apache 2才支持）。它采用更合理的方式：在内存里做压缩。对于apache 1的用户来说，可以建立一块ram磁盘，让mod_gzip在它上面写临时文件。虽然没有纯内存方式快，但也不会比往磁盘上写文件慢。</p>
<p>话虽如此，其实还是有办法完全避免压缩开销的，那就是预压缩相关静态资源，下载时由mod_gzip提供合适的压缩版本。如果把压缩添加在build过程，它就很透明了。需要压缩的文件通常很少（用不着压缩图片，因为并不能减小更多体积），只有css和js文件（和其他未压缩的静态内容）。</p>
<p>配置选项会告诉mod_gzip去哪里找到预压缩过的文件。</p>
<blockquote>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">mod_gzip_can_negotiate	Yes
mod_gzip_static_suffix	<span style="color: #339933;">.</span>gz
AddEncoding	gzip	<span style="color: #339933;">.</span>gz</pre></div></div>

</blockquote>
<p>新一点的mod_gzip版本（从1.3.26.1a开始）添加一个额外的配置选项后，就能自动预压缩文件。不过在此之前，必须确认apache有正确的权限去创建和覆盖压缩文件。</p>
<blockquote>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">mod_gzip_update_static	Yes</pre></div></div>

</blockquote>
<p>可惜，事情没那么简单。某些Netscape 4的版本（尤其是4.06-4.08）认为自己能够解释压缩内容（它们发送一个header这么说来着），但其实它们不能正确的解压缩。大多数其他版本的Netscape 4在下载压缩内容时也有各种各样的问题。所以要在服务器端探测代理类型，（如果是Netscape 4，就要）让它们得到未压缩的版本。这还算简单的。ie（版本4-6）有些更有意思的问题：当下载压缩的javascript时，有时候ie会<a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;823386&amp;Product=ie600">不正确的解压缩文件</a>，或者解压缩到一半中断，然后把这半个文件显示在客户端。如果你的应用对javascript的依赖比较大（htmlor注：比如ajax应用），那么就得避免发送压缩文件给ie。在某些情况下，一些更老的5.x版本的ie倒是能正确的收到压缩的javascript，可它们会忽略这个文件的etag header，不缓存它。（thincat友情提示：尽管压缩存在一些浏览器不兼容的现象，由于这些不能很好的支持压缩的浏览器数量现在已经非常少了，我认为这种由于浏览器导致的压缩不正常的情况可以忽略不计。这些过时的浏览器还能不能在现在流行的windows或unix环境下面安装都存在不小的问题）</p>
<p>既然gzip压缩有这么多问题，我们不妨把注意力转到另一边：不改变文件格式的压缩。现在有很多这样的javascript压缩脚本可用，大多数都用一个正则表达式驱动的语句集来减小源代码的体积。它们做的不外乎几件事：去掉注释，压缩空格，缩短私有变量名和去掉可省略的语法。</p>
<p>不幸的是，大多数脚本效果并不理想，要么压缩率相当低，要么某种情形下会把代码搞得一团糟（或者两者兼而有之）。由于对解析树的理解不完整，压缩器很难区分一句注释和一句看似注释的引用字符串。因为闭合结构的混合使用，要用正则表达式发现哪些变量是私有的并不容易，因此一些缩短变量名的技术会打乱某些闭合代码。</p>
<p>还好有个压缩器能避免这些问题：<a href="http://dojotoolkit.org/docs/compressor_system.html">dojo压缩器</a>（现成的版本在<a href="http://alex.dojotoolkit.org/shrinksafe/">这里</a>）。它使用rhino（mozilla的javascript引擎，是用java实现的）建立一个解析树，然后将其提交给文件。它能很好的减小代码体积，仅用很小的成本：因为只在build时压缩一次。由于压缩是在build过程中实现的，所以一清二楚。（既然压缩没有问题了，）我们可以在源代码里随心所欲的添加空格和注释，而不必担心影响到产品代码。</p>
<p>与javascript相比，css文件的压缩相对简单一些。由于css语法里不会有太多引用字符串（通常是url路径跟字体名），我们可以用正则表达式大刀阔斧的干掉空格（htmlor注：这句翻的最爽，哈哈）。如果确实有引用字符串的话，我们总可以把一串空格合成一个（因为不需要在url路径和字体名里查找多个空格和tab）。这样的话，一个简单的perl脚本就够了：</p>
<blockquote>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">#!/usr/bin/perl
</span>
my <span style="color: #000088;">$data</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">''</span><span style="color: #339933;">;</span>
open F<span style="color: #339933;">,</span> <span style="color: #000088;">$ARGV</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #009900;">&#93;</span> or <span style="color: #990000;">die</span> <span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span>Can<span style="color: #0000ff;">'t open source file: $!&amp;quot;;
$data .= $_ while &amp;lt;F&amp;gt;;
close F;
&nbsp;
$data =~ s!\/\*(.*?)\*\/!!g;  # 去掉注释
$data =~ s!\s+! !g;           # 压缩空格
$data =~ s!\} !}\\n!g;         # 在结束大括号后添加换行
$data =~ s!\\n$!!;             # 删除最后一个换行
$data =~ s! \{ ! {!g;         # 去除开始大括号后的空格
$data =~ s!; \}!}!g;          # 去除结束大括号前的空格
&nbsp;
print $data;</span></pre></div></div>

</blockquote>
<p>然后，就可以把单个的css文件传给脚本去压缩了。命令如下：</p>
<blockquote>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">perl compress<span style="color: #339933;">.</span>pl site<span style="color: #339933;">.</span>source<span style="color: #339933;">.</span>css <span style="color: #339933;">&amp;</span>gt<span style="color: #339933;">;</span> site<span style="color: #339933;">.</span>compress<span style="color: #339933;">.</span>css</pre></div></div>

</blockquote>
<p>做完这些简单的纯文本优化工作后，我们就能减少数据传输量多达50%了（这个量取决于你的代码格式，可能更多）。这带来了更快的用户体验。不过我们真正想做的是，尽可能避免用户请求的发生——除非确实有必要。这下HTTP缓存知识派上用场了。</p>
<h3>缓存是好东西</h3>
<p>当用户代理（如浏览器）向服务器请求一个资源时，第一次请求过后它就会缓存服务器的响应，以避免重复之后的相同请求。缓存时间的长短取决于两个因素：代理的配置和服务器的缓存控制header。所有浏览器都有不同的配置选项和处理方式，但大多数都会把一个资源至少缓存到会话结束（除非被明确告知）。</p>
<p>为了不让浏览器缓存改动频繁的页面，你很可能已经发送过header不缓存动态内容。在php中，以下两行命令可以做到：</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span>Cache<span style="color: #339933;">-</span>Control<span style="color: #339933;">:</span> private<span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span>Cache<span style="color: #339933;">-</span>Control<span style="color: #339933;">:</span> no<span style="color: #339933;">-</span><span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/cache" title="显示cache的所有日志" target="_blank">cache</a></span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;,</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>听起来太简单了？确实如此——因为有些代理（浏览器）在某些环境下将忽略这些header。要确保浏览器不缓存文档，应该更强硬一些：</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #666666; font-style: italic;"># 让它在过去就“失效”
</span><span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span>Expires<span style="color: #339933;">:</span> Mon<span style="color: #339933;">,</span> <span style="color: #cc66cc;">26</span> Jul <span style="color: #cc66cc;">1997</span> <span style="color: #208080;">05</span><span style="color: #339933;">:</span><span style="color: #208080;">00</span><span style="color: #339933;">:</span><span style="color: #208080;">00</span> GMT<span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;"># 永远是改动过的
</span><span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span>Last<span style="color: #339933;">-</span>Modified<span style="color: #339933;">:</span> <span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;.</span><span style="color: #990000;">gmdate</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span>D<span style="color: #339933;">,</span> d M Y H<span style="color: #339933;">:</span>i<span style="color: #339933;">:</span>s<span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.&amp;</span>quot<span style="color: #339933;">;</span> GMT<span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;"># HTTP/1.1
</span><span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span>Cache<span style="color: #339933;">-</span>Control<span style="color: #339933;">:</span> no<span style="color: #339933;">-</span>store<span style="color: #339933;">,</span> no<span style="color: #339933;">-</span><span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/cache" title="显示cache的所有日志" target="_blank">cache</a></span><span style="color: #339933;">,</span> must<span style="color: #339933;">-</span>revalidate<span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span>Cache<span style="color: #339933;">-</span>Control<span style="color: #339933;">:</span> post<span style="color: #339933;">-</span>check<span style="color: #339933;">=</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> pre<span style="color: #339933;">-</span>check<span style="color: #339933;">=</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;,</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;"># HTTP/1.0
</span><span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span>Pragma<span style="color: #339933;">:</span> no<span style="color: #339933;">-</span><span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/cache" title="显示cache的所有日志" target="_blank">cache</a></span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>这样，对于我们不想缓存的内容来说已经行了。但对于那些不会每次请求时都有改动的内容，应该鼓励浏览器更霸道的缓存它。“If-Modified-Since”请求header能够做到这点。如果客户端在请求中发送一个“If-Modified-Since”header，apache（或其他服务器）会以状态代码304（没改过）响应，告诉浏览器缓存已经是最新的。使用这个机制，能够避免重复发送文件给浏览器，不过仍然导致了一个HTTP请求的消耗。嗯，再想想。</p>
<p>与If-Modified-Since机制类似的是实体标记（entity tags）。在apache环境下，每个对静态文件的响应都会发出一个“ETag”header，它包含了一个由文件修改时间、文件大小和inode号生成的校验和（checksum）。在下载文件之前，浏览器会发送一个HEAD请求去检查文件的etag。可ETag跟If-Modified-Since有同样的问题：客户端仍旧需要执行HTTP请求来验证本地缓存是否有效。</p>
<p>此外，如果你使用多台服务器提供内容，得小心使用if-modified-since和etags。在两台负载平衡的服务器环境下，对一个代理（浏览器）来说，一个资源可以这次从A服务器得到，下次从B服务器得到（htmlor注：lvs负载平衡系统就是个典型的例子）。这很好，也是采用平衡负载的原因。可是，如果两台服务器给同一个文件生成了不同的etag或者文件修改日期，浏览器就无所适从了（每次都会重新下载）。默认情况下，etag是由文件的inode号生成的，而多台服务器之间文件的inode号是不同的。可以使用apache的配置选项关掉它：</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">FileETag MTime Size</pre></div></div>

<p>使用这个选项，apache将只用文件修改日期和文件大小来决定etag。很不幸，这导致了另一个问题（一样能影响if-modified-since）。既然etag依赖于修改时间，就得让时间同步。可往多台服务器上传文件时，上传时间差个一到两秒是常有的事。这样一来，两台服务器生成的etag还是不一样。当然，我们还可以改变配置，让etag的生成只取决于文件大小，但这就意味着如果文件内容变了而大小没变，etag也不会变。这可不行。</p>
<h3>缓存真是个好东西</h3>
<p>看来我们正从错误的方向入手解决问题。（现在的问题是，）这些可能的缓存策略导致了一件事情反复发生，那就是：客户端向服务器查询本地缓存是否最新。假如服务器在改动文件的时候通知客户端，客户端不就知道它的缓存是最新的了（直到接到下一次通知）？可惜天公不做美——（事实）是客户端向服务器发出请求。</p>
<p>其实，也不尽然。在获取js或css文件之前，客户端会用&lt;script&gt;或&lt;link&gt;标记向服务器发送一个请求，说明哪个页面要加载这些文件。这时候就可以用服务器的响应来通知客户端这些文件有了改动。有点含糊，说得再详细点就是：如果改变css和js文件内容的同时，也改变它们的文件名，就可以告诉客户端对url全都永久缓存——因为每个url都是唯一的。</p>
<p>假如能确定一个资源永不更改，我们就可以发出一些霸气十足的缓存header（htmlor注：这句也很有气势吧）。在php里，两行就好：</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span>Expires<span style="color: #339933;">:</span> <span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;.</span><span style="color: #990000;">gmdate</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span>D<span style="color: #339933;">,</span> d M Y H<span style="color: #339933;">:</span>i<span style="color: #339933;">:</span>s<span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;,</span> <span style="color: #990000;">time</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #cc66cc;">315360000</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.&amp;</span>quot<span style="color: #339933;">;</span> GMT<span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span>Cache<span style="color: #339933;">-</span>Control<span style="color: #339933;">:</span> max<span style="color: #339933;">-</span>age<span style="color: #339933;">=</span><span style="color: #cc66cc;">315360000</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>我们告诉浏览器这个内容在10年后（10年大概会有315,360,000秒，或多或少）过期，浏览器将会保留它10年。当然，很有可能不用php输出css和js文件（因此就不能发出header），这种情况将在稍后说明。</p>
<h3>人力有时而穷</h3>
<p>当文件内容更改时，手动去改文件名是很危险的。假如你改了文件名，模板却没有指向它？假如你改了一些模板另一些却没改？假如你改了模板却没改文件名？还有最糟的，假如你改动了文件却忘了改名或者忘了改变对它的引用？最好的结果，是用户看到老的而看不到新的内容。最坏的结果，是找不到文件，网站没法运转了。听起来这（指改动文件内容时修改url）似乎是个馊主意。</p>
<p>幸运的是，计算机做这类事情——当某种变化发生，需要相当准确地完成的、重复重复再重复的（htmlor注：番茄鸡蛋伺候～）、枯燥乏味的工作——总是十分在行。</p>
<p>这个过程（改变文件的url）没那么痛苦，因为我们根本不需要改文件名。资源的url和磁盘上文件的位置也没必要保持一致。使用apache的<a href="http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html">mod_rewrite</a>模块，可以建立简单的规则，让确定的url重定向到确定的文件。</p>
<blockquote>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">RewriteEngine on
RewriteRule ^<span style="color: #339933;">/</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">.*</span>\<span style="color: #339933;">.</span><span style="color: #009900;">&#41;</span>v<span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">-</span><span style="color:#800080;">9.</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">+</span>\<span style="color: #339933;">.</span><span style="color: #009900;">&#40;</span>css<span style="color: #339933;">|</span>js<span style="color: #339933;">|</span>gif<span style="color: #339933;">|</span>png<span style="color: #339933;">|</span>jpg<span style="color: #009900;">&#41;</span>$	<span style="color: #339933;">/</span>$<span style="color: #cc66cc;">1</span>$<span style="color: #cc66cc;">2</span>	<span style="color: #009900;">&#91;</span>L<span style="color: #009900;">&#93;</span></pre></div></div>

</blockquote>
<p>这条规则匹配任何带有指定扩展名同时含有“版本”信息（version nugget）的url，它会把这些url重定向到一个不含版本信息的路径。如下所示：</p>
<blockquote><p>
 URL			   Path<br />
/images/foo.v2.gif	-&gt; /images/foo.gif<br />
/css/main.v1.27.css	-&gt; /css/main.css<br />
/javascript/md5.v6.js	-&gt; /javascript/md5.js
</p></blockquote>
<p>使用这条规则，就可以做到不改变文件路径而更改url（因为版本号变了）。由于url变了，浏览器就认为它是另一个资源（会重新下载）。想更进一步的话，可以把我们之前说的脚本编组函数结合起来，根据需要生成一个带有版本号的&lt;script&gt;标记列表。</p>
<p>说到这里，你可能会问我，为什么不在url结尾加一个查询字符串（query string）呢（如/css/main.css?v=4）？根据HTTP缓存规格书所说，用户代理对含有查询字符串的url永不缓存。虽然ie跟firefox忽略了这点，opera和safari却没有——为了确保所有浏览器都缓存你的资源，还是不要在url里用查询字符串的好。</p>
<p>现在不移动文件就能更改url了，如果能让url自动更新就更好了。在小型的产品环境下（如果有大型的产品环境，就是开发环境了），使用模板功能可以很轻易的实现这点。这里用的是smarty，用其他模板引擎也行。</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">SMARTY<span style="color: #339933;">:</span>
<span style="color: #339933;">&lt;</span>link xhref<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;{version xsrc='/css/group.css'}&quot;</span> rel<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;stylesheet&quot;</span> type<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;text/css&quot;</span> <span style="color: #339933;">/&gt;</span>
&nbsp;
PHP<span style="color: #339933;">:</span>
<span style="color: #000000; font-weight: bold;">function</span> smarty_version<span style="color: #009900;">&#40;</span><span style="color: #000088;">$args</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>   <span style="color: #000088;">$stat</span> <span style="color: #339933;">=</span> <span style="color: #990000;">stat</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$GLOBALS</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'config'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'site_root'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">.</span><span style="color: #000088;">$args</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'src'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>   <span style="color: #000088;">$version</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$stat</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'mtime'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>   <span style="color: #b1b100;">echo</span> <span style="color: #990000;">preg_replace</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'!\.([a-z]+?)$!'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;.v<span style="color: #006699; font-weight: bold;">$version</span>.<span style="color: #000099; font-weight: bold;">\$</span>1&quot;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$args</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'src'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
OUTPUT<span style="color: #339933;">:</span>
<span style="color: #339933;">&lt;</span>link xhref<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;/css/group.v1234567890.css&quot;</span> mce_href<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;/css/group.v1234567890.css&quot;</span> rel<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;stylesheet&quot;</span> type<span style="color: #339933;">=</span><span style="color: #0000ff;">&quot;text/css&quot;</span> <span style="color: #339933;">/&gt;</span></pre></div></div>

<p>对每个链接到的资源文件，我们得到它在磁盘上的路径，检查它的mtime（文件最后修改的日期和时间），然后把这个时间当作版本号插入到url中。对于低流量的站点（它们的stat操作开销不大）或者开发环境来说，这个方案不错，但对于高容量的环境就不适用了——因为每次stat操作都要磁盘读取（导致服务器负载升高）。</p>
<p>解决方案相当简单。在大型系统中每个资源都已经有了一个版本号，就是版本控制的修订号（你们应该使用了版本控制，对吧？）。当我们建立站点准备部署的时候，可以轻易的查到每个文件的修订号，写在一个静态配置文件里。</p>
<blockquote>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$GLOBALS</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'config'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'resource_versions'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>   <span style="color: #0000ff;">'/images/foo.gif'</span>    <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'2.1'</span><span style="color: #339933;">,</span>   <span style="color: #0000ff;">'/css/main.css'</span>      <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'1.27'</span><span style="color: #339933;">,</span>   <span style="color: #0000ff;">'/javascript/md5.js'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'6.1.4'</span><span style="color: #339933;">,</span>
<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

</blockquote>
<p>当我们发布产品时，可以修改模板函数来使用版本号。</p>
<blockquote>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000000; font-weight: bold;">function</span> smarty_version<span style="color: #009900;">&#40;</span><span style="color: #000088;">$args</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>   <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$GLOBALS</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'config'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'is_dev_site'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>     <span style="color: #000088;">$stat</span> <span style="color: #339933;">=</span> <span style="color: #990000;">stat</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$GLOBALS</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'config'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'site_root'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">.</span><span style="color: #000088;">$args</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'src'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>     <span style="color: #000088;">$version</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$stat</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'mtime'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>   <span style="color: #009900;">&#125;</span><span style="color: #b1b100;">else</span><span style="color: #009900;">&#123;</span>     <span style="color: #000088;">$version</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$GLOBALS</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'config'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'resource_versions'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #000088;">$args</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'src'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>   <span style="color: #009900;">&#125;</span>   <span style="color: #b1b100;">echo</span> <span style="color: #990000;">preg_replace</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'!\.([a-z]+?)$!'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;.v<span style="color: #006699; font-weight: bold;">$version</span>.<span style="color: #000099; font-weight: bold;">\$</span>1&quot;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$args</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'src'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

</blockquote>
<p>就这样，不需要改文件名，也不需要记住改了哪些文件——当文件有新版本发布时它的url就会自动更新——有意思吧？我们就快搞定了。</p>
<h3>只欠东风</h3>
<p>之前谈到为静态文件发送超长周期（very-long-period）的缓存header时曾说过，如果不用php输出，就不能轻易的发送缓存header。很显然，有两个办法可以解决：用php输出，或者让apache来做。</p>
<p>php出马，手到擒来。我们要做的仅仅是改变rewrite规则，把静态文件指向php脚本，用php在输出文件内容之前发送header。</p>
<blockquote>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">Apache<span style="color: #339933;">:</span>
RewriteRule ^<span style="color: #339933;">/</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">.*</span>\<span style="color: #339933;">.</span><span style="color: #009900;">&#41;</span>v<span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">-</span><span style="color:#800080;">9.</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">+</span>\<span style="color: #339933;">.</span><span style="color: #009900;">&#40;</span>css<span style="color: #339933;">|</span>js<span style="color: #339933;">|</span>gif<span style="color: #339933;">|</span>png<span style="color: #339933;">|</span>jpg<span style="color: #009900;">&#41;</span>$  <span style="color: #339933;">/</span>redir<span style="color: #339933;">.</span>php?path<span style="color: #339933;">=</span>$<span style="color: #cc66cc;">1</span>$<span style="color: #cc66cc;">2</span>  <span style="color: #009900;">&#91;</span>L<span style="color: #009900;">&#93;</span>
&nbsp;
PHP<span style="color: #339933;">:</span>
<span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Expires: &quot;</span><span style="color: #339933;">.</span><span style="color: #990000;">gmdate</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;D, d M Y H:i:s&quot;</span><span style="color: #339933;">,</span> <span style="color: #990000;">time</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #cc66cc;">315360000</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot; GMT&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Cache-Control: max-age=315360000&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;"># 忽略带有“..”的路径
</span><span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #990000;">preg_match</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'!\.\.!'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span>path<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span> go_404<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #666666; font-style: italic;"># 保证路径开头是确定的目录
</span><span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #990000;">preg_match</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'!^(javascript|css|images)!'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span>path<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span> go_404<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #666666; font-style: italic;"># 文件不存在？
</span><span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #990000;">file_exists</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span>path<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span> go_404<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #666666; font-style: italic;"># 发出一个文件类型header
</span><span style="color: #000088;">$ext</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array_pop</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">explode</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'.'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span>path<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">switch</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$ext</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>   <span style="color: #b1b100;">case</span> <span style="color: #0000ff;">'css'</span><span style="color: #339933;">:</span>     <span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Content-type: text/css&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>     <span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>   <span style="color: #b1b100;">case</span> <span style="color: #0000ff;">'js'</span> <span style="color: #339933;">:</span>     <span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Content-type: text/javascript&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>     <span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>   <span style="color: #b1b100;">case</span> <span style="color: #0000ff;">'gif'</span><span style="color: #339933;">:</span>     <span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Content-type: image/gif&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>     <span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>   <span style="color: #b1b100;">case</span> <span style="color: #0000ff;">'jpg'</span><span style="color: #339933;">:</span>     <span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Content-type: image/jpeg&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>     <span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>   <span style="color: #b1b100;">case</span> <span style="color: #0000ff;">'png'</span><span style="color: #339933;">:</span>     <span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Content-type: image/png&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>     <span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>   <span style="color: #b1b100;">default</span><span style="color: #339933;">:</span>     <span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Content-type: text/plain&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #666666; font-style: italic;"># 输出文件内容
</span><span style="color: #b1b100;">echo</span> <span style="color: #990000;">implode</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">''</span><span style="color: #339933;">,</span> <span style="color: #990000;">file</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span>path<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">function</span> go_404<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>   <span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;HTTP/1.0 404 File not found&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>   <span style="color: #990000;">exit</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

</blockquote>
<p>这个方案有效，但并不出色。（因为）跟apache相比，php需要更多内存和执行时间。另外，我们还得小心防止可能由path参数传递伪造值引起的exploits。为避免这些问题，应该用apache直接发送header。rewrite规则语句允许当规则匹配时设置环境变量（environment variable），当给定的环境变量设置后，Header命令就可以添加header。结合以下两条语句，我们就把rewrite规则和header设置绑定在了一起：</p>
<blockquote>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">RewriteEngine on
RewriteRule ^<span style="color: #339933;">/</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">.*</span>\<span style="color: #339933;">.</span><span style="color: #009900;">&#41;</span>v<span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">-</span><span style="color:#800080;">9.</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">+</span>\<span style="color: #339933;">.</span><span style="color: #009900;">&#40;</span>css<span style="color: #339933;">|</span>js<span style="color: #339933;">|</span>gif<span style="color: #339933;">|</span>png<span style="color: #339933;">|</span>jpg<span style="color: #009900;">&#41;</span>$ <span style="color: #339933;">/</span>$<span style="color: #cc66cc;">1</span>$<span style="color: #cc66cc;">2</span> <span style="color: #009900;">&#91;</span>L<span style="color: #339933;">,</span>E<span style="color: #339933;">=</span>VERSIONED_FILE<span style="color: #339933;">:</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span>
&nbsp;
<span style="color: #990000;">Header</span> add <span style="color: #0000ff;">&quot;Expires&quot;</span> <span style="color: #0000ff;">&quot;Mon, 28 Jul 2014 23:30:00 GMT&quot;</span> env<span style="color: #339933;">=</span>VERSIONED_FILE
<span style="color: #990000;">Header</span> add <span style="color: #0000ff;">&quot;Cache-Control&quot;</span> <span style="color: #0000ff;">&quot;max-age=315360000&quot;</span> env<span style="color: #339933;">=</span>VERSIONED_FILE</pre></div></div>

</blockquote>
<p>考虑到apache的执行顺序，应该把rewrite规则加在主配置文件（httpd.conf）而不是目录配置文件（.htaccess）中。否则在环境变量设置之前，header行会先执行（就那没意义了）。至于header行，则可以放在两文件任何一个当中，没什么区别。</p>
<h3>眼观六路</h3>
<p>（htmlor注：多谢<a href="http://tchaikov.blogsome.com/">tchaikov</a>告知“skinning rabbits”的含义，但我不想翻的太正式，眼下的这个应该不算太离谱吧。）</p>
<p>通过结合使用以上技术，我们可以建立一个灵活的开发环境和一个快速又高性能的产品环境。当然，这离终极目标“速度”还有一段距离。有许多更深层的技术（比如分离伺服静态内容，用多域名提升并发量等）值得我们关注，包括与我们谈到的方法（建立apache过滤器，修改资源url，加上版本信息）殊途同归的其他路子。你可以留下评论，告诉我们那些你正在使用的卓有成效的技术和方法。</p>
<p>（完）</p>
<p>转载自：<a title="http://blog.htmlor.com/2006/08/03/serving_javascript_fast_chinese/" href="http://blog.htmlor.com/2006/08/03/serving_javascript_fast_chinese/">http://blog.htmlor.com/2006/08/03/serving_javascript_fast_chinese/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.xixis.net/archives/flickr-doing-a-good-thing-for-javascript.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Wordpress 提速之 Gzip 压缩</title>
		<link>http://www.xixis.net/archives/wordpress-speed-of-gzip-compression.html</link>
		<comments>http://www.xixis.net/archives/wordpress-speed-of-gzip-compression.html#comments</comments>
		<pubDate>Sun, 07 Feb 2010 14:29:23 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[gzip]]></category>
		<category><![CDATA[htaccess]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.xixis.net/?p=304</guid>
		<description><![CDATA[今天来聊下 wordpress 提速，其实关于这方面的话题网上其实蛮多的，速度对一个网站来说无疑是非常重要的，对于速度的追求也是无止境的，在这方面的表率无疑就是 shawn 了，看他博客的很多技巧都是针对速度来的……不过话说回来，速度也不是绝对的，看很多优秀的博客速度并不占优，有时候追求速度难免还会牺牲一些其它的东西！！此文仅供参考。
废话少说，关于 wordpress 的提速还是可以从很多方面入手的，因为 WP 的灵活而导致其效率和速度的下降，比如因为使用插件而在头部载入的 N 多的 JS 和 CSS 就是最为典型的，就不展开说了，今天我们只说 Gzip！
什么是 Gzip 压缩，为什么要用 Gzip 压缩
说白了就是一种压缩的技术，它将浏览器请求的文件在服务器端进行压缩，然后将文件以压缩的形式传递给浏览器，然后在浏览器端进行解压缩，还原后进行解析。而目前 99% 的浏览器都支持 Gzip ，包括 IE 4+, Netscape 4+, Opera 5+ 和所有版本的 Safari 和 Firefox ，所以大家可以放心使用。
在这里我们的压缩对象是所有的 html、JS、和 CSS 。而他的压缩效率惊人，能减小 60% 到70%。像我的博客一个 10K 的 JS 文件，用 Gzip 压缩后浏览器接收的只有 3K 大小，16K 的 CSS 文件也被压缩到了 5K，很强大吧！
怎么知道我的页面是否 Gzip 压缩过了
Gzip 的方法有很多种，而有很多的服务器是默认开启 Gzip 压缩的，不过据我所知，这种服务器少之又少。怎么确定自己的网页是否被 Gzip 呢！！这种测试的网站网上也很多，不过一般只能测试 html，这里推荐给大家一个测试的网站：Websiteoptimization，输入网址后，输入验证码，就会给出你的 Web Page Speed Report.
貌似很多的“童鞋”看错了，现在补图上来！！其中在下面的 Page Objects 中，会给出所有所加载的文件的信息。在最后一栏的信息中，如果显示：Congratulations! This file was compressed. 说明是被压缩过的，如果是：Up to **** bytes could have been saved through compression，显然是还没被压缩的。
启用 Gzip 的几种方法
是的，有几种方法，是针对不同的情况的。
1、最好的情况是你的主机采用的是 Apache 2，并安装启用了mod_deflate 模块。这样就很简单了，只需要在 .htaccess 文件中加入以下代码可以压缩 HTML, PHP, ...]]></description>
			<content:encoded><![CDATA[<p>今天来聊下 wordpress 提速，其实关于这方面的话题网上其实蛮多的，速度对一个网站来说无疑是非常重要的，对于速度的追求也是无止境的，在这方面的表率无疑就是 <a href="http://ishawn.net/">shawn</a> 了，看他博客的很多技巧都是针对速度来的……不过话说回来，速度也不是绝对的，看很多优秀的博客速度并不占优，有时候追求速度难免还会牺牲一些其它的东西！！此文仅供参考。<span id="more-304"></span></p>
<p>废话少说，关于 wordpress 的提速还是可以从很多方面入手的，因为 WP 的灵活而导致其效率和速度的下降，比如因为使用插件而在头部载入的 N 多的 JS 和 CSS 就是最为典型的，就不展开说了，今天我们只说 Gzip！</p>
<h3>什么是 Gzip 压缩，为什么要用 Gzip 压缩</h3>
<p>说白了就是一种压缩的技术，它将浏览器请求的文件在服务器端进行压缩，然后将文件以压缩的形式传递给浏览器，然后在浏览器端进行解压缩，还原后进行解析。而目前 99% 的浏览器都支持 Gzip ，包括 IE 4+, Netscape 4+, Opera 5+ 和所有版本的 Safari 和 Firefox ，所以大家可以放心使用。</p>
<p><strong>在这里我们的压缩对象是所有的 html、JS、和 CSS 。而他的压缩效率惊人，能减小 60% 到70%。像我的博客一个 10K 的 JS 文件，用 Gzip 压缩后浏览器接收的只有 3K 大小，16K 的 CSS 文件也被压缩到了 5K，很强大吧！</strong></p>
<h3>怎么知道我的页面是否 Gzip 压缩过了</h3>
<p>Gzip 的方法有很多种，而有很多的服务器是默认开启 Gzip 压缩的，不过据我所知，这种服务器少之又少。怎么确定自己的网页是否被 Gzip 呢！！这种测试的网站网上也很多，不过一般只能测试 html，这里推荐给大家一个测试的网站：<a href="http://www.websiteoptimization.com/services/analyze/">Websiteoptimization</a>，输入网址后，输入验证码，就会给出你的 Web Page Speed Report.</p>
<p>貌似很多的“童鞋”看错了，现在补图上来！！其中在下面的 Page Objects 中，会给出所有所加载的文件的信息。在最后一栏的信息中，<strong>如果显示：Congratulations! This file was compressed. 说明是被压缩过的，如果是：Up to **** bytes could have been saved through compression，显然是还没被压缩的。</strong></p>
<h3>启用 Gzip 的几种方法</h3>
<p>是的，有几种方法，是针对不同的情况的。</p>
<p>1、最好的情况是你的主机采用的是 Apache 2，并安装启用了mod_deflate 模块。这样就很简单了，只需要在 .<span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/htaccess" title="显示htaccess的所有日志" target="_blank">htaccess</a></span> 文件中加入以下代码可以压缩 HTML, PHP, JS, CSS, XML 后缀的文件。</p>
<blockquote><p>&lt;ifmodule mod_deflate.c&gt;</p>
<p>AddOutputFilterByType DEFLATE text/html text/<span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/css" title="显示css的所有日志" target="_blank">css</a></span> text/plain text/xml application/x-httpd-php application/x-<span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/javascript" title="显示javascript的所有日志" target="_blank">javascript</a></span></p>
<p>&lt;/ifmodule&gt;</p></blockquote>
<p>不过一般的主机都没有这个模块，如果你是独立主机可以自行配置。至于如何检测自己的主机是否有安装这个模块，我还真没找到办法，望高手指点。如果大家有兴趣，可能将上面的代码加好，看是否 Gzip 来试一下！以上方法来自：<a href="http://fairyfish.net/2008/04/07/gzip/">我爱水煮鱼</a></p>
<p>2、用 PHP 来压缩</p>
<p>此方法也需要 PHP 模块 zlib 的支持，不过这是一个基本的模块，一般都开启了！！所以我们主要讲下这部分。</p>
<h3>用 PHP 压缩动态页面</h3>
<p>我们先来看所谓的 html 的压缩，其实也就是由 PHP 动态生成的 html 。这样的方法有很多，我用的是 <a href="http://ishawn.net/tips/enable-gzip-function-of-hostgator.html">shawn 的方法</a> ，还有 <a href="http://wmrpd.com/blog/?p=300">几种方法</a> 供参考。</p>
<h3>用 PHP 压缩 JS 和CSS</h3>
<p><strong>关于 JS 和 CSS 的压缩也有几种方法，目前比较多的是像 K2 学习，将 JS 和 CSS 改为 .php 的方法</strong>。这个前辈已经介绍过了，请移步：<a href="http://sexywp.com/simple-guide-to-css-js-compress.htm">简述对WP博客样式表和JS脚本的压缩</a></p>
<p>这种方法的缺点是要修改每个 JS 和 CSS 文件，有点不太方便。对于将所有插件中的 JS 和CSS 都合并的博客来说还算容易，但没有合并的来说无疑是一项巨大的工程，而且插件更新也是件麻烦的事。另外看着好好的 CSS 文件变成了 PHP ，看着挺怪的，呵呵！！好处是有好的适应性，因为这 K2 用的方法！</p>
<p><strong>第二种方法是老N提供的方法</strong>：<a href="http://neolee.cn/wordpress/compress-js-css/">加速blog，压缩js,css，减省带宽</a> ，这种方法应该说比较简单，而且默认对所有的 JS 和 CSS 生效。原理就是把 JS 和 CSS 利用 .<span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/htaccess" title="显示htaccess的所有日志" target="_blank">htaccess</a></span> 交给一个 PHP 文件来处理进行压缩。</p>
<p>老N那的代码貌似复制出来有些问题，我再贴一下，呵呵：</p>
<blockquote><p>1、创建 <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/gzip" title="显示gzip的所有日志" target="_blank">gzip</a></span>.php 文件并上传到博客根目录，代码如下：</p>
<p>下载: <a href="http://www.xiaorsz.com/wp-content/plugins/coolcode/coolcode.php?p=632&amp;download=gzip.php">gzip.php</a></p></blockquote>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #339933;">&lt;!--</span>p  <span style="color: #000088;">$allowed</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>  <span style="color: #0000ff;">'<span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/css" title="显示css的所有日志" target="_blank">css</a></span>'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #0000ff;">'text/css'</span><span style="color: #339933;">,</span>  <span style="color: #0000ff;">'js'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #0000ff;">'text/<span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/javascript" title="显示javascript的所有日志" target="_blank">javascript</a></span>'</span><span style="color: #339933;">,</span>  <span style="color: #0000ff;">'html'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #0000ff;">'text/html'</span><span style="color: #339933;">,</span>  <span style="color: #0000ff;">'htm'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #0000ff;">'text/html'</span><span style="color: #339933;">,</span>  <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>  <span style="color: #000088;">$file</span> <span style="color: #339933;">=</span> <span style="color: #990000;">isset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'url'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> ? <span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'url'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">:</span> <span style="color: #009900; font-weight: bold;">null</span><span style="color: #339933;">;</span>  <span style="color: #000088;">$extension</span> <span style="color: #339933;">=</span> <span style="color: #990000;">explode</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'.'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$file</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>  <span style="color: #000088;">$extension</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array_pop</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$extension</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>  <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">isset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$allowed</span><span style="color: #009900;">&#91;</span><span style="color: #000088;">$extension</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>  <span style="color: #009900;">&#123;</span>  <span style="color: #000088;">$pos</span> <span style="color: #339933;">=</span> <span style="color: #990000;">strpos</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$file</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'..'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>  <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$pos</span> <span style="color: #339933;">===</span> <span style="color: #009900; font-weight: bold;">false</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;&amp;</span>amp<span style="color: #339933;">;</span> <span style="color: #990000;">is_file</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$file</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>  <span style="color: #009900;">&#123;</span>  <span style="color: #339933;">@</span><span style="color: #990000;">ob_start</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'ob_gzhandler'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>  <span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span>Content<span style="color: #339933;">-</span>type<span style="color: #339933;">:</span> <span style="color: #009900;">&#123;</span><span style="color: #000088;">$allowed</span><span style="color: #009900;">&#91;</span><span style="color: #000088;">$extension</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span> charset<span style="color: #339933;">:</span> UTF<span style="color: #339933;">-</span><span style="color: #cc66cc;">8</span><span style="color: #339933;">&amp;</span>quot<span style="color: #339933;">;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>  <span style="color: #990000;">readfile</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$file</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>  <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>  <span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'HTTP/1.1 404 Not Found'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>  <span style="color: #009900;">&#125;</span>  <span style="color: #009900;">&#125;</span> <span style="color: #339933;">--&gt;</span></pre></div></div>

<blockquote><p>2、更改.<span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/htaccess" title="显示htaccess的所有日志" target="_blank">htaccess</a></span>文件，在.htaccess中添加：</p>
<p>RewriteCond %{REQUEST_FILENAME} -f</p>
<p>RewriteCond %{REQUEST_FILENAME} ^.*\.(css|js|html|htm)$</p>
<p>RewriteRule ^(.*)$ <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/gzip" title="显示gzip的所有日志" target="_blank">gzip</a></span>.php?url=$1 [QSA,L]</p>
<p>就 OK 了~，的确很简单</p></blockquote>
<p>但上面两种方法还有一个问题，就是会吃服务器的资源，占用一定的 CPU 和内存，因为每次访问页面都要执行一下。但具体吃到什么程度，我就不清楚了，据说在服务器资源紧张的情况下压缩的效率会比较低，不过我想一般大型的主机提供商的主机还是不会有什么问题的。</p>
<p>第三种我们来看 <a href="http://www.thirdinfo.com/how-to-better-wp.htm">辐射鱼提供的方法</a></p>
<blockquote><p>1. 下载 <a href="http://wopus.googlecode.com/files/gzip.php">Gzip.php</a> 文件并上传到你的Wordpress安装目录</p>
<p>2. 在你的 Wordpress 根目录建立 wp-cache 文件夹并确保其可写入</p>
<p>3. 在.htaccess里面加上</p>
<p>RewriteRule (.*.css$|.*.js$) gzip.php?$1 [L]</p>
<p>即可</p></blockquote>
<p>也非常的简单，而且此种方法的优点是：自动读取并 gzip 压缩 css 和 js，访问时直接调用生成的 .gz 文件，节约带宽也不占用服务器资源，<strong>但不适用空间下安装了多个 Wordpress 的博友</strong>，可能导致二级目录下的某些 <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/wordpress" title="显示WordPress的所有日志" target="_blank">WordPress</a></span> 无法加载 CSS 样式。这些都是辐射鱼的原话，呵呵！另外就是当 CSS 和 JS 有更新时要手动清除缓存才行。符合条件的同志推荐用这种方法！！</p>
<p>转载自：<a title="http://www.xiaorsz.com/wordpress-gzip-compression-improve-the-speed/" href="http://www.xiaorsz.com/wordpress-gzip-compression-improve-the-speed/">http://www.xiaorsz.com/wordpress-gzip-compression-improve-the-speed/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.xixis.net/archives/wordpress-speed-of-gzip-compression.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress 2.7 启用 GZip压缩输出页面</title>
		<link>http://www.xixis.net/archives/wordpress-2-7-to-enable-gzip-compression-output-page.html</link>
		<comments>http://www.xixis.net/archives/wordpress-2-7-to-enable-gzip-compression-output-page.html#comments</comments>
		<pubDate>Mon, 01 Feb 2010 19:29:40 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[gzip]]></category>
		<category><![CDATA[SEO]]></category>

		<guid isPermaLink="false">http://www.xixis.net/?p=263</guid>
		<description><![CDATA[WordPress 2.5 以后就关闭了 GZip 功能。可以使用以下几种方法中适合自己的方法开启：
 
方法1：
在根目录 index.php 中找到：     define(’WP_USE_THEMES’, true);      在后面加上：      ob_start(’ob_gzhandler’);      就OK了。
也可以加一点条件判断：     if(ereg(’gzip’,$_SERVER['HTTP_ACCEPT_ENCODING'])){ //判断浏览器是否支持Gizp      if(substr($_SERVER['REQUEST_URI'],0,10)!=’/wp-content/uploads/’) //排除不需要Gzip压缩的目录      ob_start(’ob_gzhandler’); //打开Gzip压缩      }

方法2：
如果服务器支持.htaccess文件，那么在wordpress根目录的.htaccess增加以下代码，如果没有.htaccess则新建后增加代码。增加完成后上传到wordpress根目录后即可。     php_value output_handler ob_gzhandler

方法3：
通过php.ini 启用Gzip, 添加如下代码来开启gzip。
output_buffering = Off     output_handler =      zlib.output_compression = On      zlib.output_compression_level = ...]]></description>
			<content:encoded><![CDATA[<p><span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/wordpress" title="显示WordPress的所有日志" target="_blank">WordPress</a></span> 2.5 以后就关闭了 GZip 功能。可以使用以下几种方法中适合自己的方法开启：</p>
<p> <span id="more-263"></span>
<p>方法1：</p>
<blockquote><p>在根目录 index.php 中找到：     <br />define(’WP_USE_THEMES’, true);      <br />在后面加上：      <br />ob_start(’ob_gzhandler’);      <br />就OK了。</p>
<p>也可以加一点条件判断：     <br />if(ereg(’<span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/gzip" title="显示gzip的所有日志" target="_blank">gzip</a></span>’,$_SERVER['HTTP_ACCEPT_ENCODING'])){ //判断浏览器是否支持Gizp      <br />if(substr($_SERVER['REQUEST_URI'],0,10)!=’/wp-content/uploads/’) //排除不需要Gzip压缩的目录      <br />ob_start(’ob_gzhandler’); //打开Gzip压缩      <br />}</p>
</blockquote>
<p>方法2：</p>
<blockquote><p>如果服务器支持.htaccess文件，那么在wordpress根目录的.htaccess增加以下代码，如果没有.htaccess则新建后增加代码。增加完成后上传到wordpress根目录后即可。     <br />php_value output_handler ob_gzhandler</p>
</blockquote>
<p>方法3：</p>
<blockquote><p>通过php.ini 启用Gzip, 添加如下代码来开启<span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/gzip" title="显示gzip的所有日志" target="_blank">gzip</a></span>。</p>
<p>output_buffering = Off     <br />output_handler =      <br />zlib.output_compression = On      <br />zlib.output_compression_level = 6</p>
<p>zlib.output_compression_level = &lt; 任何1—9的值,1的压缩比率最低, 建议取 6&gt;</p>
</blockquote>
<p>需要注意的是如果在.htaccess文件中禁止了gzip功能的话，那在php.ini文件中开启后需在.htaccess文件中开启，否则无效。   <br />开启wordpress的gzip功能后，服务器将在页面传输前将页面压缩后传送，可以有效减少服务器的带宽使用，也可以加速页面下载。    <br />在<a href="http://www.gidnetwork.com/tools/gzip-test.php">这个网站</a>可以查看页面是否启用 GZip，以及启用后页面的压缩率。</p>
<p>我用的GoDaddy的虚拟主机，能够自己重定义 php.ini,如果是PHP5就修改php5.ini~ 用 <a href="http://www.gidnetwork.com/tools/gzip-test.php">http://www.gidnetwork.com/tools/gzip-test.php</a> 查看一下 ~压缩了72.8% ~ 速度也提升了~</p>
<p><a href="http://www.xixis.net/wp-content/uploads/2010/02/wordpress_27_gzip.jpg" rel="lightbox"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="wordpress_27_gzip" border="0" alt="wordpress_27_gzip" src="http://www.xixis.net/wp-content/uploads/2010/02/wordpress_27_gzip_thumb.jpg" width="223" height="244" /></a> </p>
<p>注意：这样就可以给所有php文件进行gzip压缩了。注意的是，这样启用gzip之后需要将程序原来的gzip功能去掉，例如discuz等的gzip不要启用，否则程序会报错。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xixis.net/archives/wordpress-2-7-to-enable-gzip-compression-output-page.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Best Practices for Speeding Up Your Web Site</title>
		<link>http://www.xixis.net/archives/best-practices-for-speeding-up-your-web-site.html</link>
		<comments>http://www.xixis.net/archives/best-practices-for-speeding-up-your-web-site.html#comments</comments>
		<pubDate>Mon, 01 Feb 2010 17:41:02 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[SEO]]></category>
		<category><![CDATA[CDN]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[gzip]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[网站加速]]></category>

		<guid isPermaLink="false">http://www.xixis.net/?p=256</guid>
		<description><![CDATA[The Exceptional Performance team has identified a number of best practices for making web pages fast. The list includes 34 best practices divided into 7 categories.
fw from:http://developer.yahoo.com/performance/rules.html
 
Filter by category:

Content 
Server 
Cookie 
CSS 
Javascript 
Images 
Mobile 
All 


 
Minimize HTTP Requests
tag: content
80% of the end-user response time is spent on the front-end. Most of this time is tied up in downloading all the components in the page: images, stylesheets, scripts, Flash, etc. Reducing the number of components in turn reduces the number of HTTP requests required to render the page. ...]]></description>
			<content:encoded><![CDATA[<p>The Exceptional Performance team has identified a number of best practices for making web pages fast. The list includes 34 best practices divided into 7 categories.</p>
<p>fw from:<a title="http://developer.yahoo.com/performance/rules.html" href="http://developer.yahoo.com/performance/rules.html">http://developer.yahoo.com/performance/rules.html</a></p>
<p> <span id="more-256"></span>
<p>Filter by category:</p>
<ul>
<li>Content </li>
<li>Server </li>
<li>Cookie </li>
<li>CSS </li>
<li>Javascript </li>
<li>Images </li>
<li>Mobile </li>
<li>All </li>
</ul>
<ol></ol>
<p> <!–-nextpage-–><br />
<h5>Minimize HTTP Requests</h5>
<p>tag: content</p>
<p>80% of the end-user response time is spent on the front-end. Most of this time is tied up in downloading all the components in the page: images, stylesheets, scripts, Flash, etc. Reducing the number of components in turn reduces the number of HTTP requests required to render the page. This is the key to faster pages. </p>
<p>One way to reduce the number of components in the page is to simplify the page&#8217;s design. But is there a way to build pages with richer content while also achieving fast response times? Here are some techniques for reducing the number of HTTP requests, while still supporting rich page designs.</p>
<p><strong>Combined files</strong> are a way to reduce the number of HTTP requests by combining all scripts into a single script, and similarly combining all CSS into a single stylesheet. Combining files is more challenging when the scripts and stylesheets vary from page to page, but making this part of your release process improves response times.</p>
</p>
<p> </a><a href="http://alistapart.com/articles/sprites"><strong>CSS Sprites</strong></a> are the preferred method for reducing the number of image requests. Combine your background images into a single image and use the CSS <code>background-image</code> and <code>background-position</code> properties to display the desired image segment.</a>
<p><a href="http://www.w3.org/TR/html401/struct/objects.html#h-13.6"><strong>Image maps</strong></a> combine multiple images into a single image. The overall size is about the same, but reducing the number of HTTP requests speeds up the page. Image maps only work if the images are contiguous in the page, such as a navigation bar. Defining the coordinates of image maps can be tedious and error prone. Using image maps for navigation is not accessible too, so it&#8217;s not recommended.</p>
<p><strong>Inline images</strong> use the <a href="http://tools.ietf.org/html/rfc2397"><code>data:</code> URL scheme</a> to embed the image data in the actual page. This can increase the size of your HTML document. Combining inline images into your (cached) stylesheets is a way to reduce HTTP requests and avoid increasing the size of your pages. Inline images are not yet supported across all major browsers.</p>
<p>Reducing the number of HTTP requests in your page is the place to start. This is the most important guideline for improving performance for first time visitors. As described in Tenni Theurer&#8217;s blog post <a href="http://yuiblog.com/blog/2007/01/04/performance-research-part-2/">Browser Cache Usage &#8211; Exposed!</a>, 40-60% of daily visitors to your site come in with an empty cache. Making your page fast for these first time visitors is key to a better user experience.</p>
<p>&#160;</p>
<h5>Use a Content Delivery Network</h5>
<p>tag: <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/server" title="显示server的所有日志" target="_blank">server</a></span></p>
<p>The user&#8217;s proximity to your web <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/server" title="显示server的所有日志" target="_blank">server</a></span> has an impact on response times. Deploying your content across multiple, geographically dispersed servers will make your pages load faster from the user&#8217;s perspective. But where should you start?</p>
<p>As a first step to implementing geographically dispersed content, don&#8217;t attempt to redesign your web application to work in a distributed architecture. Depending on the application, changing the architecture could include daunting tasks such as synchronizing session state and replicating database transactions across <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/server" title="显示server的所有日志" target="_blank">server</a></span> locations. Attempts to reduce the distance between users and your content could be delayed by, or never pass, this application architecture step. </p>
<p>Remember that 80-90% of the end-user response time is spent downloading all the components in the page: images, stylesheets, scripts, Flash, etc. This is the <em>Performance Golden Rule</em>. Rather than starting with the difficult task of redesigning your application architecture, it&#8217;s better to first disperse your static content. This not only achieves a bigger reduction in response times, but it&#8217;s easier thanks to content delivery networks.</p>
<p>A content delivery network (<span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/cdn" title="显示CDN的所有日志" target="_blank">CDN</a></span>) is a collection of web servers distributed across multiple locations to deliver content more efficiently to users. The server selected for delivering content to a specific user is typically based on a measure of network proximity. For example, the server with the fewest network hops or the server with the quickest response time is chosen.</p>
<p>Some large Internet companies own their own <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/cdn" title="显示CDN的所有日志" target="_blank">CDN</a></span>, but it&#8217;s cost-effective to use a <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/cdn" title="显示CDN的所有日志" target="_blank">CDN</a></span> service provider, such as <a href="http://www.akamai.com/">Akamai Technologies</a>, <a href="http://www.mirror-image.com/">Mirror Image Internet</a>, or <a href="http://www.limelightnetworks.com/">Limelight Networks</a>. For start-up companies and private web sites, the cost of a CDN service can be prohibitive, but as your target audience grows larger and becomes more global, a CDN is necessary to achieve fast response times. At Yahoo!, properties that moved static content off their application web servers to a CDN improved end-user response times by 20% or more. Switching to a CDN is a relatively easy code change that will dramatically improve the speed of your web site.</p>
<p>&#160;</p>
<p> <!–-nextpage-–><br />
<h5>Add an Expires or a Cache-Control Header</h5>
<p>tag: server</p>
<p>There are two things in this rule:</p>
<ul>
<li>For static components: implement &quot;Never expire&quot; policy by setting far future <code>Expires</code> header </li>
<li>For dynamic components: use an appropriate <code>Cache-Control</code> header to help the browser with conditional requests </li>
</ul>
<p>Web page designs are getting richer and richer, which means more scripts, stylesheets, images, and Flash in the page. A first-time visitor to your page may have to make several HTTP requests, but by using the Expires header you make those components cacheable. This avoids unnecessary HTTP requests on subsequent page views. Expires headers are most often used with images, but they should be used on <em>all</em> components including scripts, stylesheets, and Flash components.</p>
<p>Browsers (and proxies) use a cache to reduce the number and size of HTTP requests, making web pages load faster. A web server uses the Expires header in the HTTP response to tell the client how long a component can be cached. This is a far future Expires header, telling the browser that this response won&#8217;t be stale until April 15, 2010. </p>
<pre>      Expires: Thu, 15 Apr 2010 20:00:00 GMT</pre>
<p>If your server is Apache, use the ExpiresDefault directive to set an expiration date relative to the current date. This example of the ExpiresDefault directive sets the Expires date 10 years out from the time of the request.</p>
<pre>      ExpiresDefault &quot;access plus 10 years&quot;</pre>
<p>Keep in mind, if you use a far future Expires header you have to change the component&#8217;s filename whenever the component changes. At Yahoo! we often make this step part of the build process: a version number is embedded in the component&#8217;s filename, for example, yahoo_2.0.6.js.</p>
<p>Using a far future Expires header affects page views only after a user has already visited your site. It has no effect on the number of HTTP requests when a user visits your site for the first time and the browser&#8217;s cache is empty. Therefore the impact of this performance improvement depends on how often users hit your pages with a primed cache. (A &quot;primed cache&quot; already contains all of the components in the page.) We <a href="http://yuiblog.com/blog/2007/01/04/performance-research-part-2/">measured this at Yahoo!</a> and found the number of page views with a primed cache is 75-85%. By using a far future Expires header, you increase the number of components that are cached by the browser and re-used on subsequent page views without sending a single byte over the user&#8217;s Internet connection.</p>
<p>&#160;</p>
<h5>Gzip Components</h5>
<p>tag: server</p>
<p>The time it takes to transfer an HTTP request and response across the network can be significantly reduced by decisions made by front-end engineers. It&#8217;s true that the end-user&#8217;s bandwidth speed, Internet service provider, proximity to peering exchange points, etc. are beyond the control of the development team. But there are other variables that affect response times. Compression reduces response times by reducing the size of the HTTP response.</p>
<p>Starting with HTTP/1.1, web clients indicate support for compression with the Accept-Encoding header in the HTTP request.</p>
<pre>      Accept-Encoding: <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/gzip" title="显示gzip的所有日志" target="_blank">gzip</a></span>, deflate</pre>
<p>If the web server sees this header in the request, it may compress the response using one of the methods listed by the client. The web server notifies the web client of this via the Content-Encoding header in the response.</p>
<pre>      Content-Encoding: <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/gzip" title="显示gzip的所有日志" target="_blank">gzip</a></span></pre>
<p>Gzip is the most popular and effective compression method at this time. It was developed by the GNU project and standardized by <a href="http://www.ietf.org/rfc/rfc1952.txt">RFC 1952</a>. The only other compression format you&#8217;re likely to see is deflate, but it&#8217;s less effective and less popular. </p>
<p>Gzipping generally reduces the response size by about 70%. Approximately 90% of today&#8217;s Internet traffic travels through browsers that claim to support gzip. If you use Apache, the module configuring gzip depends on your version: Apache 1.3 uses <a href="http://sourceforge.net/projects/mod-gzip/">mod_gzip</a> while Apache 2.x uses <a href="http://httpd.apache.org/docs/2.0/mod/mod_deflate.html">mod_deflate</a>. </p>
<p>There are known issues with browsers and proxies that may cause a mismatch in what the browser expects and what it receives with regard to compressed content. Fortunately, these edge cases are dwindling as the use of older browsers drops off. The Apache modules help out by adding appropriate Vary response headers automatically. </p>
<p>Servers choose what to gzip based on file type, but are typically too limited in what they decide to compress. Most web sites gzip their HTML documents. It&#8217;s also worthwhile to gzip your scripts and stylesheets, but many web sites miss this opportunity. In fact, it&#8217;s worthwhile to compress any text response including XML and JSON. Image and PDF files should not be gzipped because they are already compressed. Trying to gzip them not only wastes CPU but can potentially increase file sizes. </p>
<p>Gzipping as many file types as possible is an easy way to reduce page weight and accelerate the user experience.</p>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Put Stylesheets at the Top</h5>
<p>tag: <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/css" title="显示css的所有日志" target="_blank">css</a></span></p>
<p>While researching performance at Yahoo!, we discovered that moving stylesheets to the document HEAD makes pages <em>appear</em> to be loading faster. This is because putting stylesheets in the HEAD allows the page to render progressively. </p>
<p>Front-end engineers that care about performance want a page to load progressively; that is, we want the browser to display whatever content it has as soon as possible. This is especially important for pages with a lot of content and for users on slower Internet connections. The importance of giving users visual feedback, such as progress indicators, has been well researched and <a href="http://www.useit.com/papers/responsetime.html">documented</a>. In our case the HTML page is the progress indicator! When the browser loads the page progressively the header, the navigation bar, the logo at the top, etc. all serve as visual feedback for the user who is waiting for the page. This improves the overall user experience.</p>
<p>The problem with putting stylesheets near the bottom of the document is that it prohibits progressive rendering in many browsers, including Internet Explorer. These browsers block rendering to avoid having to redraw elements of the page if their styles change. The user is stuck viewing a blank white page. </p>
<p>The <a href="http://www.w3.org/TR/html4/struct/links.html#h-12.3">HTML specification</a> clearly states that stylesheets are to be included in the HEAD of the page: &quot;Unlike A, [LINK] may only appear in the HEAD section of a document, although it may appear any number of times.&quot; Neither of the alternatives, the blank white screen or flash of unstyled content, are worth the risk. The optimal solution is to follow the HTML specification and load your stylesheets in the document HEAD.</p>
<p>&#160;</p>
<h5>Put Scripts at the Bottom</h5>
<p>tag: javascript</p>
<p>The problem caused by scripts is that they block parallel downloads. The <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4">HTTP/1.1 specification</a> suggests that browsers download no more than two components in parallel per hostname. If you serve your images from multiple hostnames, you can get more than two downloads to occur in parallel. While a script is downloading, however, the browser won&#8217;t start any other downloads, even on different hostnames. </p>
<p>In some situations it&#8217;s not easy to move scripts to the bottom. If, for example, the script uses <code>document.write</code> to insert part of the page&#8217;s content, it can&#8217;t be moved lower in the page. There might also be scoping issues. In many cases, there are ways to workaround these situations.</p>
<p>An alternative suggestion that often comes up is to use deferred scripts. The <code>DEFER</code> attribute indicates that the script does not contain document.write, and is a clue to browsers that they can continue rendering. Unfortunately, Firefox doesn&#8217;t support the <code>DEFER</code> attribute. In Internet Explorer, the script may be deferred, but not as much as desired. If a script can be deferred, it can also be moved to the bottom of the page. That will make your web pages load faster.</p>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Avoid CSS Expressions</h5>
<p>tag: <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/css" title="显示css的所有日志" target="_blank">css</a></span></p>
<p>CSS expressions are a powerful (and dangerous) way to set CSS properties dynamically. They&#8217;re supported in Internet Explorer, starting with <a href="http://msdn.microsoft.com/workshop/author/dhtml/overview/recalc.asp">version 5</a>. As an example, the background color could be set to alternate every hour using CSS expressions.</p>
<pre>      background-color: expression( (new Date()).getHours()%2 ? &quot;#B8D4FF&quot; : &quot;#F08A00&quot; );</pre>
<p>As shown here, the <code>expression</code> method accepts a JavaScript expression. The CSS property is set to the result of evaluating the JavaScript expression. The <code>expression</code> method is ignored by other browsers, so it is useful for setting properties in Internet Explorer needed to create a consistent experience across browsers.</p>
<p>The problem with expressions is that they are evaluated more frequently than most people expect. Not only are they evaluated when the page is rendered and resized, but also when the page is scrolled and even when the user moves the mouse over the page. Adding a counter to the CSS expression allows us to keep track of when and how often a CSS expression is evaluated. Moving the mouse around the page can easily generate more than 10,000 evaluations.</p>
<p>One way to reduce the number of times your CSS expression is evaluated is to use one-time expressions, where the first time the expression is evaluated it sets the style property to an explicit value, which replaces the CSS expression. If the style property must be set dynamically throughout the life of the page, using event handlers instead of CSS expressions is an alternative approach. If you must use CSS expressions, remember that they may be evaluated thousands of times and could affect the performance of your page.</p>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Make JavaScript and CSS External</h5>
<p>tag: javascript, <span class='wp_keywordlink_affiliate'><a href="http://www.xixis.net/archives/tag/css" title="显示css的所有日志" target="_blank">css</a></span></p>
<p>Many of these performance rules deal with how external components are managed. However, before these considerations arise you should ask a more basic question: Should JavaScript and CSS be contained in external files, or inlined in the page itself?</p>
<p>Using external files in the real world generally produces faster pages because the JavaScript and CSS files are cached by the browser. JavaScript and CSS that are inlined in HTML documents get downloaded every time the HTML document is requested. This reduces the number of HTTP requests that are needed, but increases the size of the HTML document. On the other hand, if the JavaScript and CSS are in external files cached by the browser, the size of the HTML document is reduced without increasing the number of HTTP requests. </p>
<p>The key factor, then, is the frequency with which external JavaScript and CSS components are cached relative to the number of HTML documents requested. This factor, although difficult to quantify, can be gauged using various metrics. If users on your site have multiple page views per session and many of your pages re-use the same scripts and stylesheets, there is a greater potential benefit from cached external files. </p>
<p>Many web sites fall in the middle of these metrics. For these sites, the best solution generally is to deploy the JavaScript and CSS as external files. The only exception where inlining is preferable is with home pages, such as <a href="http://www.yahoo.com">Yahoo!&#8217;s front page</a> and <a href="http://my.yahoo.com">My Yahoo!</a>. Home pages that have few (perhaps only one) page view per session may find that inlining JavaScript and CSS results in faster end-user response times.</p>
<p>For front pages that are typically the first of many page views, there are techniques that leverage the reduction of HTTP requests that inlining provides, as well as the caching benefits achieved through using external files. One such technique is to inline JavaScript and CSS in the front page, but dynamically download the external files after the page has finished loading. Subsequent pages would reference the external files that should already be in the browser&#8217;s cache.</p>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Reduce DNS Lookups</h5>
<p>tag: content</p>
<p>The Domain Name System (DNS) maps hostnames to IP addresses, just as phonebooks map people&#8217;s names to their phone numbers. When you type www.yahoo.com into your browser, a DNS resolver contacted by the browser returns that server&#8217;s IP address. DNS has a cost. It typically takes 20-120 milliseconds for DNS to lookup the IP address for a given hostname. The browser can&#8217;t download anything from this hostname until the DNS lookup is completed. </p>
<p>DNS lookups are cached for better performance. This caching can occur on a special caching server, maintained by the user&#8217;s ISP or local area network, but there is also caching that occurs on the individual user&#8217;s computer. The DNS information remains in the operating system&#8217;s DNS cache (the &quot;DNS Client service&quot; on Microsoft Windows). Most browsers have their own caches, separate from the operating system&#8217;s cache. As long as the browser keeps a DNS record in its own cache, it doesn&#8217;t bother the operating system with a request for the record.</p>
<p>Internet Explorer caches DNS lookups for 30 minutes by default, as specified by the <code>DnsCacheTimeout</code> registry setting. Firefox caches DNS lookups for 1 minute, controlled by the <code>network.dnsCacheExpiration</code> configuration setting. (Fasterfox changes this to 1 hour.)</p>
<p>When the client&#8217;s DNS cache is empty (for both the browser and the operating system), the number of DNS lookups is equal to the number of unique hostnames in the web page. This includes the hostnames used in the page&#8217;s URL, images, script files, stylesheets, Flash objects, etc. Reducing the number of unique hostnames reduces the number of DNS lookups. </p>
<p>Reducing the number of unique hostnames has the potential to reduce the amount of parallel downloading that takes place in the page. Avoiding DNS lookups cuts response times, but reducing parallel downloads may increase response times. My guideline is to split these components across at least two but no more than four hostnames. This results in a good compromise between reducing DNS lookups and allowing a high degree of parallel downloads.</p>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Minify JavaScript and CSS</h5>
<p>tag: javascript, css</p>
<p>Minification is the practice of removing unnecessary characters from code to reduce its size thereby improving load times. When code is minified all comments are removed, as well as unneeded white space characters (space, newline, and tab). In the case of JavaScript, this improves response time performance because the size of the downloaded file is reduced. Two popular tools for minifying JavaScript code are <a href="http://crockford.com/javascript/jsmin">JSMin</a> and <a href="http://developer.yahoo.com/yui/compressor/">YUI Compressor</a>. The YUI compressor can also minify CSS.</p>
<p>Obfuscation is an alternative optimization that can be applied to source code. It&#8217;s more complex than minification and thus more likely to generate bugs as a result of the obfuscation step itself. In a survey of ten top U.S. web sites, minification achieved a 21% size reduction versus 25% for obfuscation. Although obfuscation has a higher size reduction, minifying JavaScript is less risky.</p>
<p>In addition to minifying external scripts and styles, inlined <code>&lt;script&gt;</code> and <code>&lt;style&gt;</code> blocks can and should also be minified. Even if you gzip your scripts and styles, minifying them will still reduce the size by 5% or more. As the use and size of JavaScript and CSS increases, so will the savings gained by minifying your code.</p>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Avoid Redirects</h5>
<p>tag: content</p>
<p>Redirects are accomplished using the 301 and 302 status codes. Here&#8217;s an example of the HTTP headers in a 301 response:</p>
<pre>      HTTP/1.1 301 Moved Permanently
      Location: http://example.com/newuri
      Content-Type: text/html</pre>
<p>The browser automatically takes the user to the URL specified in the <code>Location</code> field. All the information necessary for a redirect is in the headers. The body of the response is typically empty. Despite their names, neither a 301 nor a 302 response is cached in practice unless additional headers, such as <code>Expires</code> or <code>Cache-Control</code>, indicate it should be. The meta refresh tag and JavaScript are other ways to direct users to a different URL, but if you must do a redirect, the preferred technique is to use the standard 3xx HTTP status codes, primarily to ensure the back button works correctly.</p>
<p>The main thing to remember is that redirects slow down the user experience. Inserting a redirect between the user and the HTML document delays everything in the page since nothing in the page can be rendered and no components can start being downloaded until the HTML document has arrived.</p>
<p>One of the most wasteful redirects happens frequently and web developers are generally not aware of it. It occurs when a trailing slash (/) is missing from a URL that should otherwise have one. For example, going to <a href="http://astrology.yahoo.com/astrology">http://astrology.yahoo.com/astrology</a> results in a 301 response containing a redirect to <a href="http://astrology.yahoo.com/astrology/">http://astrology.yahoo.com/astrology/</a> (notice the added trailing slash). This is fixed in Apache by using <code>Alias</code> or <code>mod_rewrite</code>, or the <code>DirectorySlash</code> directive if you&#8217;re using Apache handlers.</p>
<p>Connecting an old web site to a new one is another common use for redirects. Others include connecting different parts of a website and directing the user based on certain conditions (type of browser, type of user account, etc.). Using a redirect to connect two web sites is simple and requires little additional coding. Although using redirects in these situations reduces the complexity for developers, it degrades the user experience. Alternatives for this use of redirects include using <code>Alias</code> and <code>mod_rewrite</code> if the two code paths are hosted on the same server. If a domain name change is the cause of using redirects, an alternative is to create a CNAME (a DNS record that creates an alias pointing from one domain name to another) in combination with <code>Alias</code> or <code>mod_rewrite</code>.</p>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Remove Duplicate Scripts</h5>
<p>tag: javascript</p>
<p>It hurts performance to include the same JavaScript file twice in one page. This isn&#8217;t as unusual as you might think. A review of the ten top U.S. web sites shows that two of them contain a duplicated script. Two main factors increase the odds of a script being duplicated in a single web page: team size and number of scripts. When it does happen, duplicate scripts hurt performance by creating unnecessary HTTP requests and wasted JavaScript execution.</p>
<p>Unnecessary HTTP requests happen in Internet Explorer, but not in Firefox. In Internet Explorer, if an external script is included twice and is not cacheable, it generates two HTTP requests during page loading. Even if the script is cacheable, extra HTTP requests occur when the user reloads the page.</p>
<p>In addition to generating wasteful HTTP requests, time is wasted evaluating the script multiple times. This redundant JavaScript execution happens in both Firefox and Internet Explorer, regardless of whether the script is cacheable.</p>
<p>One way to avoid accidentally including the same script twice is to implement a script management module in your templating system. The typical way to include a script is to use the SCRIPT tag in your HTML page.</p>
<pre>      &lt;script type=&quot;text/javascript&quot; src=&quot;menu_1.0.17.js&quot;&gt;&lt;/script&gt;</pre>
<p>An alternative in PHP would be to create a function called <code>insertScript</code>.</p>
<pre>      &lt;?php insertScript(&quot;menu.js&quot;) ?&gt;</pre>
<p>In addition to preventing the same script from being inserted multiple times, this function could handle other issues with scripts, such as dependency checking and adding version numbers to script filenames to support far future Expires headers.</p>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Configure ETags</h5>
<p>tag: server</p>
<p>Entity tags (ETags) are a mechanism that web servers and browsers use to determine whether the component in the browser&#8217;s cache matches the one on the origin server. (An &quot;entity&quot; is another word a &quot;component&quot;: images, scripts, stylesheets, etc.) ETags were added to provide a mechanism for validating entities that is more flexible than the last-modified date. An ETag is a string that uniquely identifies a specific version of a component. The only format constraints are that the string be quoted. The origin server specifies the component&#8217;s ETag using the <code>ETag</code> response header.</p>
<pre>      HTTP/1.1 200 OK
      Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
      ETag: &quot;10c24bc-4ab-457e1c1f&quot;
      Content-Length: 12195</pre>
<p>Later, if the browser has to validate a component, it uses the <code>If-None-Match</code> header to pass the ETag back to the origin server. If the ETags match, a 304 status code is returned reducing the response by 12195 bytes for this example. </p>
<pre>      GET /i/yahoo.gif HTTP/1.1
      Host: us.yimg.com
      If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT
      If-None-Match: &quot;10c24bc-4ab-457e1c1f&quot;
      HTTP/1.1 304 Not Modified</pre>
<p>The problem with ETags is that they typically are constructed using attributes that make them unique to a specific server hosting a site. ETags won&#8217;t match when a browser gets the original component from one server and later tries to validate that component on a different server, a situation that is all too common on Web sites that use a cluster of servers to handle requests. By default, both Apache and IIS embed data in the ETag that dramatically reduces the odds of the validity test succeeding on web sites with multiple servers. </p>
<p>The ETag format for Apache 1.3 and 2.x is <code>inode-size-timestamp</code>. Although a given file may reside in the same directory across multiple servers, and have the same file size, permissions, timestamp, etc., its inode is different from one server to the next. </p>
<p>IIS 5.0 and 6.0 have a similar issue with ETags. The format for ETags on IIS is <code>Filetimestamp:ChangeNumber</code>. A <code>ChangeNumber</code> is a counter used to track configuration changes to IIS. It&#8217;s unlikely that the <code>ChangeNumber</code> is the same across all IIS servers behind a web site. </p>
<p>The end result is ETags generated by Apache and IIS for the exact same component won&#8217;t match from one server to another. If the ETags don&#8217;t match, the user doesn&#8217;t receive the small, fast 304 response that ETags were designed for; instead, they&#8217;ll get a normal 200 response along with all the data for the component. If you host your web site on just one server, this isn&#8217;t a problem. But if you have multiple servers hosting your web site, and you&#8217;re using Apache or IIS with the default ETag configuration, your users are getting slower pages, your servers have a higher load, you&#8217;re consuming greater bandwidth, and proxies aren&#8217;t caching your content efficiently. Even if your components have a far future <code>Expires</code> header, a conditional GET request is still made whenever the user hits Reload or Refresh. </p>
<p>If you&#8217;re not taking advantage of the flexible validation model that ETags provide, it&#8217;s better to just remove the ETag altogether. The <code>Last-Modified</code> header validates based on the component&#8217;s timestamp. And removing the ETag reduces the size of the HTTP headers in both the response and subsequent requests. This <a href="http://support.microsoft.com/?id=922733">Microsoft Support article</a> describes how to remove ETags. In Apache, this is done by simply adding the following line to your Apache configuration file: </p>
<pre>      FileETag none</pre>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Make Ajax Cacheable</h5>
<p>tag: content</p>
<p>One of the cited benefits of Ajax is that it provides instantaneous feedback to the user because it requests information asynchronously from the backend web server. However, using Ajax is no guarantee that the user won&#8217;t be twiddling his thumbs waiting for those asynchronous JavaScript and XML responses to return. In many applications, whether or not the user is kept waiting depends on how Ajax is used. For example, in a web-based email client the user will be kept waiting for the results of an Ajax request to find all the email messages that match their search criteria. It&#8217;s important to remember that &quot;asynchronous&quot; does not imply &quot;instantaneous&quot;.</p>
<p>To improve performance, it&#8217;s important to optimize these Ajax responses. The most important way to improve the performance of Ajax is to make the responses cacheable, as discussed in <a href="http://developer.yahoo.com/performance/rules.html#expires">Add an Expires or a Cache-Control Header</a>. Some of the other rules also apply to Ajax:</p>
<ul>
<li><a href="http://developer.yahoo.com/performance/rules.html#gzip">Gzip Components</a> </li>
<li><a href="http://developer.yahoo.com/performance/rules.html#dns_lookups">Reduce DNS Lookups</a> </li>
<li><a href="http://developer.yahoo.com/performance/rules.html#minify">Minify JavaScript</a> </li>
<li><a href="http://developer.yahoo.com/performance/rules.html#redirects">Avoid Redirects</a> </li>
<li><a href="http://developer.yahoo.com/performance/rules.html#etags">Configure ETags</a> </li>
</ul>
<p>Let&#8217;s look at an example. A Web 2.0 email client might use Ajax to download the user&#8217;s address book for autocompletion. If the user hasn&#8217;t modified her address book since the last time she used the email web app, the previous address book response could be read from cache if that Ajax response was made cacheable with a future Expires or Cache-Control header. The browser must be informed when to use a previously cached address book response versus requesting a new one. This could be done by adding a timestamp to the address book Ajax URL indicating the last time the user modified her address book, for example, <code>&amp;t=1190241612</code>. If the address book hasn&#8217;t been modified since the last download, the timestamp will be the same and the address book will be read from the browser&#8217;s cache eliminating an extra HTTP roundtrip. If the user has modified her address book, the timestamp ensures the new URL doesn&#8217;t match the cached response, and the browser will request the updated address book entries.</p>
<p>Even though your Ajax responses are created dynamically, and might only be applicable to a single user, they can still be cached. Doing so will make your Web 2.0 apps faster.</p>
<p>&#160;</p>
<h5>Flush the Buffer Early</h5>
<p>tag: server</p>
<p>When users request a page, it can take anywhere from 200 to 500ms for the backend server to stitch together the HTML page. During this time, the browser is idle as it waits for the data to arrive. In PHP you have the function <a href="http://php.net/flush">flush()</a>. It allows you to send your partially ready HTML response to the browser so that the browser can start fetching components while your backend is busy with the rest of the HTML page. The benefit is mainly seen on busy backends or light frontends. </p>
<p>A good place to consider flushing is right after the HEAD because the HTML for the head is usually easier to produce and it allows you to include any CSS and JavaScript files for the browser to start fetching in parallel while the backend is still processing. </p>
<p>Example:</p>
<pre>      ... &lt;!-- css, js --&gt;
    &lt;/head&gt;
    &lt;?php flush(); ?&gt;
    &lt;body&gt;
      ... &lt;!-- content --&gt;</pre>
<p><a href="http://search.yahoo.com">Yahoo! search</a> pioneered research and real user testing to prove the benefits of using this technique.</p>
<p>&#160;</p>
<h5>Use GET for AJAX Requests</h5>
<p>tag: server</p>
<p>The <a href="http://mail.yahoo.com">Yahoo! Mail</a> team found that when using <code>XMLHttpRequest</code>, POST is implemented in the browsers as a two-step process: sending the headers first, then sending data. So it&#8217;s best to use GET, which only takes one TCP packet to send (unless you have a lot of cookies). The maximum URL length in IE is 2K, so if you send more than 2K data you might not be able to use GET. </p>
<p>An interesting side affect is that POST without actually posting any data behaves like GET. Based on the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html">HTTP specs</a>, GET is meant for retrieving information, so it makes sense (semantically) to use GET when you&#8217;re only requesting data, as opposed to sending data to be stored server-side. </p>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Post-load Components</h5>
<p>tag: content</p>
<p>You can take a closer look at your page and ask yourself: &quot;What&#8217;s absolutely required in order to render the page initially?&quot;. The rest of the content and components can wait. </p>
<p>JavaScript is an ideal candidate for splitting before and after the onload event. For example if you have JavaScript code and libraries that do drag and drop and animations, those can wait, because dragging elements on the page comes after the initial rendering. Other places to look for candidates for post-loading include hidden content (content that appears after a user action) and images below the fold. </p>
<p>Tools to help you out in your effort: <a href="http://developer.yahoo.com/yui/imageloader/">YUI Image Loader</a> allows you to delay images below the fold and the <a href="http://developer.yahoo.com/yui/get/">YUI Get utility</a> is an easy way to include JS and CSS on the fly. For an example in the wild take a look at <a href="http://www.yahoo.com">Yahoo! Home Page</a> with Firebug&#8217;s Net Panel turned on. </p>
<p>It&#8217;s good when the performance goals are inline with other web development best practices. In this case, the idea of progressive enhancement tells us that JavaScript, when supported, can improve the user experience but you have to make sure the page works even without JavaScript. So after you&#8217;ve made sure the page works fine, you can enhance it with some post-loaded scripts that give you more bells and whistles such as drag and drop and animations. </p>
<p>&#160;</p>
<h5>Preload Components</h5>
<p>tag: content</p>
<p>Preload may look like the opposite of post-load, but it actually has a different goal. By preloading components you can take advantage of the time the browser is idle and request components (like images, styles and scripts) you&#8217;ll need in the future. This way when the user visits the next page, you could have most of the components already in the cache and your page will load much faster for the user. </p>
<p>There are actually several types of preloading: </p>
<ul>
<li><em>Unconditional</em> preload &#8211; as soon as onload fires, you go ahead and fetch some extra components. Check google.com for an example of how a sprite image is requested onload. This sprite image is not needed on the google.com homepage, but it is needed on the consecutive search result page. </li>
<li><em>Conditional</em> preload &#8211; based on a user action you make an educated guess where the user is headed next and preload accordingly. On <a href="http://search.yahoo.com">search.yahoo.com</a> you can see how some extra components are requested after you start typing in the input box. </li>
<li><em>Anticipated</em> preload &#8211; preload in advance before launching a redesign. It often happens after a redesign that you hear: &quot;The new site is cool, but it&#8217;s slower than before&quot;. Part of the problem could be that the users were visiting your old site with a full cache, but the new one is always an empty cache experience. You can mitigate this side effect by preloading some components before you even launched the redesign. Your old site can use the time the browser is idle and request images and scripts that will be used by the new site </li>
</ul>
<p>&#160;</p>
<h5>Reduce the Number of DOM Elements</h5>
<p>tag: content</p>
<p>A complex page means more bytes to download and it also means slower DOM access in JavaScript. It makes a difference if you loop through 500 or 5000 DOM elements on the page when you want to add an event handler for example. </p>
<p>A high number of DOM elements can be a symptom that there&#8217;s something that should be improved with the markup of the page without necessarily removing content. Are you using nested tables for layout purposes? Are you throwing in more <code>&lt;div&gt;</code>s only to fix layout issues? Maybe there&#8217;s a better and more semantically correct way to do your markup. </p>
<p>A great help with layouts are the <a href="http://developer.yahoo.com/yui/">YUI CSS utilities</a>: grids.css can help you with the overall layout, fonts.css and reset.css can help you strip away the browser&#8217;s defaults formatting. This is a chance to start fresh and think about your markup, for example use <code>&lt;div&gt;</code>s only when it makes sense semantically, and not because it renders a new line. </p>
<p>The number of DOM elements is easy to test, just type in Firebug&#8217;s console:<br />
  <br /><code>document.getElementsByTagName('*').length</code></p>
<p>And how many DOM elements are too many? Check other similar pages that have good markup. For example the <a href="http://www.yahoo.com">Yahoo! Home Page</a> is a pretty busy page and still under 700 elements (HTML tags). </p>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Split Components Across Domains</h5>
<p>tag: content</p>
<p>Splitting components allows you to maximize parallel downloads. Make sure you&#8217;re using not more than 2-4 domains because of the DNS lookup penalty. For example, you can host your HTML and dynamic content on <code>www.example.org</code> and split static components between <code>static1.example.org</code> and <code>static2.example.org</code></p>
<p>For more information check &quot;<a href="http://yuiblog.com/blog/2007/04/11/performance-research-part-4/">Maximizing Parallel Downloads in the Carpool Lane</a>&quot; by Tenni Theurer and Patty Chi. </p>
<p>&#160;</p>
<h5>Minimize the Number of iframes</h5>
<p>tag: content</p>
<p>Iframes allow an HTML document to be inserted in the parent document. It&#8217;s important to understand how iframes work so they can be used effectively. </p>
<p><code>&lt;iframe&gt;</code> pros: </p>
<ul>
<li>Helps with slow third-party content like badges and ads </li>
<li>Security sandbox </li>
<li>Download scripts in parallel </li>
</ul>
<p><code>&lt;iframe&gt;</code> cons: </p>
<ul>
<li>Costly even if blank </li>
<li>Blocks page onload </li>
<li>Non-semantic </li>
</ul>
<p>&#160;</p>
<h5>No 404s</h5>
<p>tag: content</p>
<p>HTTP requests are expensive so making an HTTP request and getting a useless response (i.e. 404 Not Found) is totally unnecessary and will slow down the user experience without any benefit. </p>
<p>Some sites have helpful 404s &quot;Did you mean X?&quot;, which is great for the user experience but also wastes server resources (like database, etc). Particularly bad is when the link to an external JavaScript is wrong and the result is a 404. First, this download will block parallel downloads. Next the browser may try to parse the 404 response body as if it were JavaScript code, trying to find something usable in it. </p>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Reduce Cookie Size</h5>
<p>tag: cookie</p>
<p>HTTP cookies are used for a variety of reasons such as authentication and personalization. Information about cookies is exchanged in the HTTP headers between web servers and browsers. It&#8217;s important to keep the size of cookies as low as possible to minimize the impact on the user&#8217;s response time. </p>
<p>For more information check <a href="http://yuiblog.com/blog/2007/03/01/performance-research-part-3/">&quot;When the Cookie Crumbles&quot;</a> by Tenni Theurer and Patty Chi. The take-home of this research: </p>
<ul>
<li>Eliminate unnecessary cookies </li>
<li>Keep cookie sizes as low as possible to minimize the impact on the user response time </li>
<li>Be mindful of setting cookies at the appropriate domain level so other sub-domains are not affected </li>
<li>Set an Expires date appropriately. An earlier Expires date or none removes the cookie sooner, improving the user response time </li>
</ul>
<p>&#160;</p>
<h5>Use Cookie-free Domains for Components</h5>
<p>tag: cookie</p>
<p>When the browser makes a request for a static image and sends cookies together with the request, the server doesn&#8217;t have any use for those cookies. So they only create network traffic for no good reason. You should make sure static components are requested with cookie-free requests. Create a subdomain and host all your static components there. </p>
<p>If your domain is <code>www.example.org</code>, you can host your static components on <code>static.example.org</code>. However, if you&#8217;ve already set cookies on the top-level domain <code>example.org</code> as opposed to <code>www.example.org</code>, then all the requests to <code>static.example.org</code> will include those cookies. In this case, you can buy a whole new domain, host your static components there, and keep this domain cookie-free. Yahoo! uses <code>yimg.com</code>, YouTube uses <code>ytimg.com</code>, Amazon uses <code>images-amazon.com</code> and so on. </p>
<p>Another benefit of hosting static components on a cookie-free domain is that some proxies might refuse to cache the components that are requested with cookies. On a related note, if you wonder if you should use example.org or www.example.org for your home page, consider the cookie impact. Omitting www leaves you no choice but to write cookies to <code>*.example.org</code>, so for performance reasons it&#8217;s best to use the www subdomain and write the cookies to that subdomain. </p>
<p>&#160;</p>
<h5>Minimize DOM Access</h5>
<p>tag: javascript</p>
<p>Accessing DOM elements with JavaScript is slow so in order to have a more responsive page, you should: </p>
<ul>
<li>Cache references to accessed elements </li>
<li>Update nodes &quot;offline&quot; and then add them to the tree </li>
<li>Avoid fixing layout with JavaScript </li>
</ul>
<p>For more information check the YUI theatre&#8217;s <a href="http://yuiblog.com/blog/2007/12/20/video-lecomte/">&quot;High Performance Ajax Applications&quot;</a> by Julien Lecomte. </p>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Develop Smart Event Handlers</h5>
<p>tag: javascript</p>
<p>Sometimes pages feel less responsive because of too many event handlers attached to different elements of the DOM tree which are then executed too often. That&#8217;s why using <em>event delegation</em> is a good approach. If you have 10 buttons inside a <code>div</code>, attach only one event handler to the div wrapper, instead of one handler for each button. Events bubble up so you&#8217;ll be able to catch the event and figure out which button it originated from. </p>
<p>You also don&#8217;t need to wait for the onload event in order to start doing something with the DOM tree. Often all you need is the element you want to access to be available in the tree. You don&#8217;t have to wait for all images to be downloaded. <code>DOMContentLoaded</code> is the event you might consider using instead of onload, but until it&#8217;s available in all browsers, you can use the <a href="http://developer.yahoo.com/yui/event/">YUI Event</a> utility, which has an <code><a href="http://developer.yahoo.com/yui/event/#onavailable">onAvailable</a></code> method. </p>
<p>For more information check the YUI theatre&#8217;s <a href="http://yuiblog.com/blog/2007/12/20/video-lecomte/">&quot;High Performance Ajax Applications&quot;</a> by Julien Lecomte. </p>
<p>&#160;</p>
<h5>Choose &lt;link&gt; over @import</h5>
<p>tag: css</p>
<p>One of the previous best practices states that CSS should be at the top in order to allow for progressive rendering. </p>
<p>In IE <code>@import</code> behaves the same as using <code>&lt;link&gt;</code> at the bottom of the page, so it&#8217;s best not to use it. </p>
<p>&#160;</p>
<h5>Avoid Filters</h5>
<p>tag: css</p>
<p>The IE-proprietary <code>AlphaImageLoader</code> filter aims to fix a problem with semi-transparent true color PNGs in IE versions &lt; 7. The problem with this filter is that it blocks rendering and freezes the browser while the image is being downloaded. It also increases memory consumption and is applied per element, not per image, so the problem is multiplied. </p>
<p>The best approach is to avoid <code>AlphaImageLoader</code> completely and use gracefully degrading PNG8 instead, which are fine in IE. If you absolutely need <code>AlphaImageLoader</code>, use the underscore hack <code>_filter</code> as to not penalize your IE7+ users. </p>
<p>&#160;</p>
<h5>Optimize Images</h5>
<p>tag: images</p>
<p>After a designer is done with creating the images for your web page, there are still some things you can try before you FTP those images to your web server. </p>
<ul>
<li>You can check the GIFs and see if they are using a palette size corresponding to the number of colors in the image. Using <a href="http://www.imagemagick.org">imagemagick</a> it&#8217;s easy to check using
<p><code>identify -verbose image.gif</code> </p>
<p>When you see an image useing 4 colors and a 256 color &quot;slots&quot; in the palette, there is room for improvement. </li>
<li>Try converting GIFs to PNGs and see if there is a saving. More often than not, there is. Developers often hesitate to use PNGs due to the limited support in browsers, but this is now a thing of the past. The only real problem is alpha-transparency in true color PNGs, but then again, GIFs are not true color and don&#8217;t support variable transparency either. So anything a GIF can do, a palette PNG (PNG8) can do too (except for animations). This simple imagemagick command results in totally safe-to-use PNGs:<br />
    <br /><code>convert image.gif image.png</code> </p>
<p>&quot;All we are saying is: Give PiNG a Chance!&quot; </li>
<li>Run <a href="http://pmt.sourceforge.net/pngcrush/">pngcrush</a> (or any other PNG optimizer tool) on all your PNGs. Example:
<p><code>pngcrush image.png -rem alla -reduce -brute result.png</code> </li>
<li>Run jpegtran on all your JPEGs. This tool does lossless JPEG operations such as rotation and can also be used to optimize and remove comments and other useless information (such as EXIF information) from your images.<br />
    <br /><code>jpegtran -copy none -optimize -perfect src.jpg dest.jpg</code> </li>
</ul>
<p>&#160;</p>
<p><!–-nextpage-–></p>
<h5>Optimize CSS Sprites</h5>
<p>tag: images</p>
<ul>
<li>Arranging the images in the sprite horizontally as opposed to vertically usually results in a smaller file size. </li>
<li>Combining similar colors in a sprite helps you keep the color count low, ideally under 256 colors so to fit in a PNG8. </li>
<li>&quot;Be mobile-friendly&quot; and don&#8217;t leave big gaps between the images in a sprite. This doesn&#8217;t affect the file size as much but requires less memory for the user agent to decompress the image into a pixel map. 100&#215;100 image is 10 thousand pixels, where 1000&#215;1000 is 1 million pixels </li>
</ul>
<p>&#160;</p>
<h5>Don&#8217;t Scale Images in HTML</h5>
<p>tag: images</p>
<p>Don&#8217;t use a bigger image than you need just because you can set the width and height in HTML. If you need<br />
  <br /><code>&lt;img width=&quot;100&quot; height=&quot;100&quot; src=&quot;mycat.jpg&quot; alt=&quot;My Cat&quot; /&gt;</code> </p>
<p>then your image (mycat.jpg) should be 100&#215;100px rather than a scaled down 500&#215;500px image. </p>
<p>&#160;</p>
<h5>Make favicon.ico Small and Cacheable</h5>
<p>tag: images</p>
<p>The favicon.ico is an image that stays in the root of your server. It&#8217;s a necessary evil because even if you don&#8217;t care about it the browser will still request it, so it&#8217;s better not to respond with a <code>404 Not Found</code>. Also since it&#8217;s on the same server, cookies are sent every time it&#8217;s requested. This image also interferes with the download sequence, for example in IE when you request extra components in the onload, the favicon will be downloaded before these extra components. </p>
<p>So to mitigate the drawbacks of having a favicon.ico make sure: </p>
<ul>
<li>It&#8217;s small, preferably under 1K. </li>
<li>Set Expires header with what you feel comfortable (since you cannot rename it if you decide to change it). You can probably safely set the Expires header a few months in the future. You can check the last modified date of your current favicon.ico to make an informed decision. </li>
</ul>
<p><a href="http://www.imagemagick.org">Imagemagick</a> can help you create small favicons </p>
<p>&#160;</p>
<h5>Keep Components under 25K</h5>
<p>tag: mobile</p>
<p>This restriction is related to the fact that iPhone won&#8217;t cache components bigger than 25K. Note that this is the <em>uncompressed</em> size. This is where minification is important because gzip alone may not be sufficient. </p>
<p>For more information check &quot;<a href="http://yuiblog.com/blog/2008/02/06/iphone-cacheability/">Performance Research, Part 5: iPhone Cacheability &#8211; Making it Stick</a>&quot; by Wayne Shea and Tenni Theurer. </p>
<p>&#160;</p>
<h5>Pack Components into a Multipart Document</h5>
<p>tag: mobile</p>
<p>Packing components into a multipart document is like an email with attachments, it helps you fetch several components with one HTTP request (remember: HTTP requests are expensive). When you use this technique, first check if the user agent supports it (iPhone does not). </p>
<p>top</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xixis.net/archives/best-practices-for-speeding-up-your-web-site.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

