首页 > web前端 > html教程 > 关于yahoo军规的详细介绍

关于yahoo军规的详细介绍

黄舟
发布: 2017-07-24 13:52:03
原创
1169 人浏览过

yahoo军规一共分735

 

1.尽量减少HTTP请求数

 

分类: 内容

 

80%的终端用户响应时间都花在了前端上,其中大部分时间都在下载页面上的各种组件:图片,样式表,脚本,Flash等等。减少组件数必然能够减少页面提交的HTTP请求数。这是让页面更快的关键。

 

减少页面组件数的一种方式是简化页面设计。但有没有一种方法可以在构建复杂的页面同时加快响应时间呢?嗯,确实有鱼和熊掌兼得的办法。

 

合并文件是通过把所有脚本放在一个文件中的方式来减少请求数的,当然,也可以合并所有的CSS。如果各个页面的脚本和样式不一样的话,合并文件就是一项比较麻烦的工作了,但把这个作为站点发布过程的一部分确实可以提高响应时间。

 

CSS Sprites 是减少图片请求数量的首选方式。把背景图片都整合到一张图片中,然后用CSSbackground-image background-position 属性来定位要显示的部分。

 

图像映射 可以把多张图片合并成单张图片,总大小是一样的,但减少了请求数并加速了页面加载。图片地图只有在图像在页面中连续的时候才有用,比如导航条。给image map设置坐标的过程既无聊又容易出错,用image map来做导航也不容易,所以不推荐用这种方式。

 

行内图片(Base64编码)用 data: URL模式 来把图片嵌入页面。这样会增加HTML文件的大小,把行内图片放在(缓存的)样式表中是个好办法,而且成功避免了页面变“重”。但目前主流浏览器并不能很好地支持行内图片。

 

减少页面的HTTP请求数是个起点,这是提升站点首次访问速度的重要指导原则。就像Tenni Theurer的博客 Browser Cache Usage Exposed! 里写到的,40%60%的访客在访问你的站点时,缓存都是空的。所以,加快页面首次访问速度对提高用户体验是极其重要的。

 

2.使用CDNContent Delivery Network

 

分类: 服务器

 

用户与服务器的物理距离对响应时间也有影响。把内容部署在多个地理位置分散的服务器上能让用户更快地载入页面。但具体要怎么做呢?

 

实现内容在地理位置上分散的第一步是:不要尝试去重新设计你的web应用程序来适应分布式结构。这取决于应用程序,改变结构可能包括一些让人望而生畏的任务,比如同步会话状态和跨服务器复制数据库事务(翻译可能不准确)。缩短用户和内容之间距离的提议可能被推迟,或者根本不可能通过,就是因为这个难题。

 

记住终端用户80%90%的响应时间都花在下载页面组件上了:图片,样式,脚本,Flash等等,这是 业绩黄金法则 。最好先分散静态内容,而不是一开始就重新设计应用程序结构。这不仅能够大大减少响应时间,还更容易表现出CDN的功劳。

 

内容分发网络(CDN)是一组分散在不同地理位置的web服务器,用来给用户更高效地发送内容。典型地,选择用来发送内容的服务器是基于网络距离的衡量标准的。例如:选跳数(hop)最少的或者响应时间最快的服务器。

 

一些互联网公司巨头拥有他们自己的CDN,但用一个CDN服务提供者是比较划算的,比如 Akamai Technologies EdgeCast ,或者 level3 。对刚刚起步的公司和个人网站来说,CDN服务的成本是很高的,但如果你的用户群却越来越大,越来越全球化,那么用CDN来换取更快的响应时间还是很有必要的。在Yahoo!,把静态内容从应用程序的web服务器搬到CDN(包括上面提到的3rd partyYahoo自己的 CDN )能够提高终端用户20%甚至更多的响应时间。换到CDN是一个相当简单的代码变更,但这将急剧提升站点的响应速度。

 

3.添上Expires或者Cache-Control HTTP

 

分类: 服务器

 

这条规则有两个方面:

 

对于静态组件:通过设置一个遥远的将来时间作为 Expires 来实现永不失效

多余动态组件:用合适的 Cache-Control HTTP头来让浏览器进行条件性的请求

网页设计越来越丰富,这意味着页面里有更多的脚本,图片和Flash。站点的新访客可能还是不得不提交几个HTTP请求,但通过使用有效期能让组件变得可缓存,这避免了在接下来的浏览过程中不必要的HTTP请求。有效期HTTP头通常被用在图片上,但它们应该用在 所有 组件上,包括脚本、样式和Flash组件。

 

浏览器(和代理)用缓存来减少HTTP请求的数目和大小,让页面能够更快加载。web服务器通过有效期HTTP响应头来告诉客户端,页面的各个组件应该被缓存多久。用一个遥远的将来时间做有效期,告诉浏览器这个响应在2010415日前不会改变。

 

Expires: Thu, 15 Apr 2010 20:00:00 GMT

如果你用的是Apache服务器,用ExpiresDefault指令来设置相对于当前日期的有效期。下面的例子设置了从请求时间起10年的有效期:

 

ExpiresDefault "access plus 10 years"

记住,如果你用一个遥远的未来时间做有效期,就不得不在组件发生变化后及时修改组件的文件名。在Yahoo!,我们经常把这一步作为构建过程的一部分:把版本号内嵌在组件的文件名里,例如:yahoo_2.0.6.js

 

用一个遥远的未来时间做有效期HTTP头,只有在用户已经访问过站点之后才会影响页面视图。如果是新访客或者浏览器的缓存被清空时,对HTTP请求的数量并没有影响。因此这种性能提升取决于已缓存各个组件的用户访问站点的频率。我们 在Yahoo!测量了这个数据 ,发现已缓存各个组件的页面访问量(PV)占75%85%。通过把一个遥远的未来时间作为有效期HTTP头,增加了被浏览器缓存的组件数量,在后续页面访问量中不需要用Internet连接多发送哪怕一个字节。

 

4.Gzip组件

 

分类: 服务器

 

前端工程师可以想办法明显地缩短通过网络传输HTTP请求和响应的时间。毫无疑问,终端用户的带宽速度,网络服务商,对等交换点的距离等等,都是开发团队所无法控制的。但还有别的能够影响响应时间的因素,压缩可以通过减少HTTP响应的大小来缩短响应时间。

 

HTTP/1.1开始,web客户端就有了支持压缩的Accept-Encoding HTTP请求头。

 

Accept-Encoding: gzip, deflate

如果web服务器看到这个请求头,它就会用客户端列出的一种方式来压缩响应。web服务器通过Content-Encoding相应头来通知客户端。

 

Content-Encoding: gzip

Gzip是目前最常见的高效压缩方法,由GNU项目开发并被 RFC 1952 标准化。唯一一个你可能会看到的其它压缩格式是deflate,但它效率不高而且并不常见。

 

Gzipping一般能够把响应压缩到70%左右,目前大约90%的通过浏览器的网络传输都支持gzip。如果是Apache服务器,配置gzip的模块取决于版本:Apache 1.3mod_gzip Apache 2.xmod_deflate 模块。

 

浏览器和代理的某些因素可能会引起浏览器所期望的和它收到的压缩内容不匹配。幸运的是,随着老旧浏览器的淘汰,这些极少遇到的情况正在逐渐减少,而且Apache模块可以通过自动添加合适的Vary响应头来帮你搞定。

 

服务器会根据文件类型来决定要不要用gzip压缩,但这非常有限。大多数网站都用gzip压缩HTML文件,其实压缩脚本,样式表也是不错的选择,但很多网站却错失了这个机会。其实,可以压缩任何文本内容,包括XMLJSON,而图片和PDF不用压缩,因为它们已经被压缩过了,再用gzip压缩不仅浪费CPU还可能会越压越大。

 

尽可能多地用gzip压缩能够给页面减肥,这也是提升用户体验最简单的方法。

 

5.把样式表放在顶部

 

分类: css

 

Yahoo!研究性能的时候,我们发现把样式表放到文档的HEAD部分能让页面 看起来 加载地更快。这是因为把样式表放在head里能让页面逐步渲染。

 

关注性能的前端工程师想让页面逐步渲染。也就是说,我们想让浏览器尽快显示已有内容,这在页面上有一大堆内容或者用户网速很慢时显得尤为重要。给用户显示反馈(比如进度指标)的重要性已经被广泛研究过,并且被 记录 下来了。在我们的例子中,HTML页面就是进度指标!当浏览器逐渐加载页面头部,导航条,顶部logo等等内容的时候,这些都被正在等待页面加载的用户当作反馈,能够提高整体用户体验。

 

在很多浏览器(包括IE)中,把样式表放在HTML文档底部都会阻止页面逐渐渲染。这些浏览器阻塞渲染过程,以避免因为样式变动而重绘页面元素,用户这时就只能盯着空白页面。

 

HTML官方文档 清楚地描述了样式表应该放在页面的HEAD里面:”Unlike A, [LINK] may only appear in the HEAD section of a document, although it may appear any number of times.”(不像a标签,link标签可能只出现在HEAD部分,虽然它能可以出现任意多次)。空白屏幕或者没有样式的falsh内容都是不可取的。理想方案就是遵循HTML官方文档,把样式表放在HTML文档的HEAD部分。

 

6.把脚本放在底部

 

分类: javascript

 

脚本会阻塞并行下载, HTTP/1.1官方文档 建议浏览器每个主机名下并行下载的组件数不要超过两个,如果图片来自多个主机名,并行下载的数量就可以超过两个。如果脚本正在下载,浏览器就不开始任何其它下载任务,即使是在不同主机名下的。

 

有时候,并不容易把脚本移动到底部。举个例子,如果脚本是用 document.write 插入到页面内容中的,就没办法再往下移了。还可能存在作用域问题,在多数情况下,这些问题都是可以解决的。

 

一个常见的建议是用推迟(deferred)脚本,有 DEFER 属性的脚本意味着不能含有document.write,并且提示浏览器告诉他们可以继续渲染。不幸的是,Firefox不支持 DEFER 属性。在IE中,脚本可能被推迟,但不尽如人意。如果脚本可以推迟,我们就可以把它放到页面底部,页面就可以更快地载入。

 

7.避免使用CSS表达式

 

分类: css

 

CSS表达式动态设置CSS属性,是一种强大又危险的方式。从IE5开始支持,但 从IE8起就不推荐使用了 。例如,可以用CSS表达式把背景颜色设置成按小时交替的:

 

 

上面的代码中, expression 方法可以接受一个JavaScript表达式。CSS属性会被设置成表达式的计算结果。 expression 方法会被其它浏览器忽略,所以只有想办法实现跨浏览器的与IE一致的用户体验才有用。

 

表达式最大的问题是它们经常被重复计算,比我们想象的次数还要多。不仅仅是页面渲染和调整大小的时候,在页面被滚动,甚至用户在页面上移动鼠标时都会重新计算表达式。给CSS表达式添加一个计数器就可以追踪它重新计算的时间和频率,而在页面上动动鼠标就可以引发10000多次重新计算。

 

减少CSS表达式重新计算的一种方式就是用一次性表达式,即在表达式第一次计算后就把样式属性设置成一个明确的值,换掉表达式。如果必须要在页面的整个生命周期中动态设置样式属性,可以用事件处理器来代替CSS表达式。如果必须使用CSS表达式,要记得它们可能会被重复计算上千次,从而影响整个页面的性能。

 

8.JavaScriptCSS放到外面

 

分类: javascript, css

 

很多性能原则都是关于如何管理外部组件的,然而,在这些顾虑出现之前你应该问一个更基础的问题:应该把JavaScriptCSS放到外部文件中还是直接写在页面里?

 

实际上,用外部文件可以让页面更快,因为JavaScriptCSS文件会被缓存在浏览器。HTML文档中的行内JavaScriptCSS在每次请求该HTML文档的时候都会重新下载。这样做减少了所需的HTTP请求数,但增加了HTML文档的大小。另一方面,如果JavaScriptCSS在外部文件中,并且已经被浏览器缓存起来了,那么我们就成功地把HTML文档变小了,而且还没有增加HTTP请求数。

 

关键因素是,外部文件被缓存的频率和页面被请求数量之间的关系。尽管这个因素很难量化,但我们还是可以用各种各样的指标来衡量。如果用户的每个会话中都有多次页面访问,那么相同的脚本和样式表就可以被多个页面复用,缓存的外部文件就会带来巨大的好处。

 

很多站点在度量中都处于中等水平,对这些站点来说,一般最好的解决方案就是把JavaScriptCSS部署为外部文件。唯一的例外是主页上行内方式优先,例如 Yahoo!的首页 和 My Yahoo! 。在每个会话中访问量比较少的主页会发现行内JavaScriptCSS能让终端用户的响应时间更快。

 

对典型的站点来说,首页是众多访问量的开始,有很多技术可以对减少HTTP请求起到杠杆作用,就像用外部文件缓存的好处一样。这样的一种技术就是在首页用行内JavaScriptCSS,但在页面载入完成之后动态加载外部文件,这样后续的页面所需的外部文件就已经被放到浏览器的缓存里了。

 

9.减少DNS查找

 

分类: 内容

 

域名系统建立了主机名和IP地址间的映射,就像电话簿上人名和号码的映射一样。当你在浏览器输入www.yahoo.com的时候,浏览器就会联系DNS解析器返回服务器的IP地址。DNS是有成本的,它需要20120毫秒去查找给定主机名的IP地址。在DNS查找完成之前,浏览器无法从主机名下载任何东西。

 

DNS查找被缓存起来更高效,由用户的ISP(网络服务提供商)或者本地网络存在一个特殊的缓存服务器上,但还可以缓存在个人用户的计算机上。DNS信息被保存在操作系统的DNS cache(微软Windows上的”DNS客户端服务”)里。大多数浏览器有独立于操作系统的自己的cache。只要浏览器在自己的cache里还保留着这条记录,它就不会向操作系统查询DNS

 

IE默认缓存DNS查找30分钟,写在 DnsCacheTimeout 注册表设置中。Firefox缓存1分钟,可以用 network.dnsCacheExpiration 配置项设置。(Fasterfox把缓存时间改成了1小时 P.S. FasterfoxFF的一个提速插件)

 

如果客户端的DNS cache是空的(包括浏览器的和操作系统的),DNS查找数等于页面上不同的主机名数,包括页面URL,图片,脚本文件,样式表,Flash对象等等组件中的主机名,减少不同的主机名就可以减少DNS查找。

 

减少不同主机名的数量同时也减少了页面能够并行下载的组件数量,避免DNS查找削减了响应时间,而减少并行下载数量却增加了响应时间。我的原则是把组件分散在24个主机名下,这是同时减少DNS查找和允许高并发下载的折中方案。

 

10.压缩JavaScriptCSS

 

分类: javascript, css

 

压缩具体来说就是从代码中去除不必要的字符以减少大小,从而提升加载速度。代码最小化就是去掉所有注释和不必要的空白字符(空格,换行和tab)。在JavaScript中这样做能够提高响应性能,因为要下载的文件变小了。两个最常用的JavaScript代码压缩工具是 JSMin YUI Compressor YUI compressor还可以压缩CSS

 

混淆是一种可选的源码优化措施,要比压缩更复杂,所以混淆过程也更容易产生bug。在对美国前十的网站调查中,压缩可以缩小21%,而混淆能缩小25%。虽然混淆的缩小程度更高,但比压缩风险更大。

 

除了压缩外部脚本和样式,行内的 <script> <span style="font-family: 宋体">和 </span><span style="font-family: Calibri"><style> </span><span style="font-family: 宋体">块也可以压缩。即使启用了</span><span style="font-family: Calibri">gzip</span><span style="font-family: 宋体">模块,先进行压缩也能够缩小</span><span style="font-family: Calibri">5%</span><span style="font-family: 宋体">或者更多的大小。</span><span style="font-family: Calibri">JavaScript</span><span style="font-family: 宋体">和</span><span style="font-family: Calibri">CSS</span><span style="font-family: 宋体">的用处越来越多,所以压缩代码会有不错的效果。</span></p> <p> </p> <p><strong>11.<span style="font-family: 宋体">避免重定向</span></strong></p> <p> </p> <p><span style="font-family: 宋体">分类</span>: <span style="font-family: 宋体">内容</span></p> <p> </p> <p><span style="font-family: 宋体">重定向用</span>301<span style="font-family: 宋体">和</span><span style="font-family: Calibri">302</span><span style="font-family: 宋体">状态码,下面是一个有</span><span style="font-family: Calibri">301</span><span style="font-family: 宋体">状态码的</span><span style="font-family: Calibri">HTTP</span><span style="font-family: 宋体">头:</span></p> <p> </p> <p>HTTP/1.1 301 Moved Permanently</p> <p>      Location:</p> <p>      Content-Type: text/html</p> <p><span style="font-family: 宋体">浏览器会自动跳转到</span> Location <span style="font-family: 宋体">域指明的</span><span style="font-family: Calibri">URL</span><span style="font-family: 宋体">。重定向需要的所有信息都在</span><span style="font-family: Calibri">HTTP</span><span style="font-family: 宋体">头部,而响应体一般是空的。其实额外的</span><span style="font-family: Calibri">HTTP</span><span style="font-family: 宋体">头,比如 </span><span style="font-family: Calibri">Expires </span><span style="font-family: 宋体">和 </span><span style="font-family: Calibri">Cache-Control </span><span style="font-family: 宋体">也表示重定向。除此之外还有别的跳转方式:</span><span style="font-family: Calibri">refresh</span><span style="font-family: 宋体">元标签和</span><span style="font-family: Calibri">JavaScript</span><span style="font-family: 宋体">,但如果你必须得做重定向,最好用标准的</span><span style="font-family: Calibri">3xxHTTP</span><span style="font-family: 宋体">状态码,主要是为了让返回按钮能正常使用。</span></p> <p> </p> <p><span style="font-family: 宋体">牢记重定向会拖慢用户体验,在用户和</span>HTML<span style="font-family: 宋体">文档之间插入重定向会延迟页面上的所有东西,页面无法渲染,组件也无法开始下载,直到</span><span style="font-family: Calibri">HTML</span><span style="font-family: 宋体">文档被送达浏览器。</span></p> <p> </p> <p><span style="font-family: 宋体">有一种常见的极其浪费资源的重定向,而且</span>web<span style="font-family: 宋体">开发人员一般都意识不到这一点,就是</span><span style="font-family: Calibri">URL</span><span style="font-family: 宋体">尾部缺少一个斜线的时候。例如,跳转到 </span><span style="font-family: Calibri"> </span><span style="font-family: 宋体">会返回一个重定向到 </span><span style="font-family: Calibri"> </span><span style="font-family: 宋体">的</span><span style="font-family: Calibri">301</span><span style="font-family: 宋体">响应(注意添在尾部的斜线)。在</span><span style="font-family: Calibri">Apache</span><span style="font-family: 宋体">中可以用 </span><span style="font-family: Calibri">Alias </span><span style="font-family: 宋体">, </span><span style="font-family: Calibri">mod_rewrite </span><span style="font-family: 宋体">或者 </span><span style="font-family: Calibri">DirectorySlash </span><span style="font-family: 宋体">指令来取消不必要的重定向。</span></p> <p> </p> <p><span style="font-family: 宋体">重定向最常见的用途是把旧站点连接到新的站点,还可以连接同一站点的不同部分,针对用户的不同情况(浏览器类型,用户帐号类型等等)做一些处理。用重定向来连接两个网站是最简单的,只需要少量的额外代码。虽然在这些时候使用重定向减少了开发人员的开发复杂度,但降低了用户体验。一种替代方案是用</span> Alias <span style="font-family: 宋体">和 </span><span style="font-family: Calibri">mod_rewrite </span><span style="font-family: 宋体">,前提是两个代码路径都在相同的服务器上。如果是因为域名变化而使用了重定向,就可以创建一条</span><span style="font-family: Calibri">CNAME</span><span style="font-family: 宋体">(创建一个指向另一个域名的</span><span style="font-family: Calibri">DNS</span><span style="font-family: 宋体">记录作为别名)结合 </span><span style="font-family: Calibri">Alias </span><span style="font-family: 宋体">或者 </span><span style="font-family: Calibri">mod_rewrite </span><span style="font-family: 宋体">指令。</span></p> <p> </p> <p><strong>12.<span style="font-family: 宋体">去除重复脚本</span></strong></p> <p> </p> <p><span style="font-family: 宋体">分类</span>: javascript</p> <p> </p> <p><span style="font-family: 宋体">页面含有重复的脚本文件会影响性能,这可能和你想象的不一样。在对美国前</span>10<span style="font-family: 宋体">大</span><span style="font-family: Calibri">web</span><span style="font-family: 宋体">站点的评审中,发现只有</span><span style="font-family: Calibri">2</span><span style="font-family: 宋体">个站点含有重复脚本。两个主要原因增加了在单一页面中出现重复脚本的几率:团队大小和脚本数量。在这种情况下,重复脚本会创建不必要的</span><span style="font-family: Calibri">HTTP</span><span style="font-family: 宋体">请求,执行无用的</span><span style="font-family: Calibri">JavaScript</span><span style="font-family: 宋体">代码,而影响页面性能。</span></p> <p> </p> <p>IE<span style="font-family: 宋体">会产生不必要的</span><span style="font-family: Calibri">HTTP</span><span style="font-family: 宋体">请求,而</span><span style="font-family: Calibri">Firefox</span><span style="font-family: 宋体">不会。在</span><span style="font-family: Calibri">IE</span><span style="font-family: 宋体">中,如果一个不可缓存的外部脚本被页面引入了两次,它会在页面加载时产生两个</span><span style="font-family: Calibri">HTTP</span><span style="font-family: 宋体">请求。即使脚本是可缓存的,在用户重新加载页面时也会产生额外的</span><span style="font-family: Calibri">HTTP</span><span style="font-family: 宋体">请求。</span></p> <p> </p> <p><span style="font-family: 宋体">除了产生没有意义的</span>HTTP<span style="font-family: 宋体">请求之外,多次对脚本求值也会浪费时间。因为无论脚本是否可缓存,在</span><span style="font-family: Calibri">Firefox</span><span style="font-family: 宋体">和</span><span style="font-family: Calibri">IE</span><span style="font-family: 宋体">中都会执行冗余的</span><span style="font-family: Calibri">JavaScript</span><span style="font-family: 宋体">代码。</span></p> <p> </p> <p><span style="font-family: 宋体">避免不小心把相同脚本引入两次的一种方法就是在模版系统中实现脚本管理模块。典型的脚本引入方法就是在</span>HTML<span style="font-family: 宋体">页面中用</span><span style="font-family: Calibri">SCRIPT</span><span style="font-family: 宋体">标签:</span></p> <p> </p> <p><script type="text/javascript" src="menu_1.0.17.js?1.1.11"></script>

PHP中一个可选方案是创建一个叫 insertScript 的函数:

<?php insertScript("menu.js?1.1.11") ?>
登录后复制

除了防止相同脚本被多次引入,这个函数还可以解决脚本相关的其它问题,比如依赖性检查和给脚本文件名添加版本号来支持“永久”有效期HTTP头。

13.配置ETags

分类: 服务器

实体标签(ETags),是服务器和浏览器用来决定浏览器缓存中组件与源服务器中的组件是否匹配的一种机制(“实体”也就是组件:图片,脚本,样式表等等)。添加ETags可以提供一种实体验证机制,比最后修改日期更加灵活。一个ETag是一个字符串,作为一个组件某一具体版本的唯一标识符。唯一的格式约束是字符串必须用引号括起来,源服务器用相应头中的 ETag 来指定组件的ETag

HTTP/1.1 200 OK

Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT

ETag: "10c24bc-4ab-457e1c1f"

Content-Length: 12195

然后,如果浏览器必须验证一个组件,它用 If-None-Match 请求头来把ETag传回源服务器。如果ETags匹配成功,会返回一个304状态码,这样就减少了12195个字节的响应体。

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: "10c24bc-4ab-457e1c1f"

HTTP/1.1 304 Not Modified

ETags存在的问题是它们是由特定服务器构造的,所以如果浏览器从一个服务器获取最初的组件,然后想验证另一个服务器上的相同组件,ETags是无法匹配成功的,而用一群服务器处理请求在web站点中又非常普遍。默认情况下,ApacheIIS会在ETag中嵌入数据,以大大降低在多服务器站点上有效性测试成功的几率。

Apache 1.32.xETag的格式是 inode-size-timestamp 。就算给定的文件可能在多个服务器的相同目录下,而且文件大小、访问权限、时间戳等等全部相同,它的i节点(P.S. inodeUNIX中的索引文件)在不同服务器中也不一样。

IIS5.06.0也都存在类似的问题。IISETags的格式是 Filetimestamp:ChangeNumber ChangeNumber 是一个用来追踪IIS配置变更的计数器。 一个站点在不同的IIS服务器上的 ChangeNumber 是不可能相同的。

最终结果是ApacheIIS为完全相同的组件生成的ETags无法跨浏览器匹配,如果ETags不匹配,用户就无法收到为又小又快的304响应设计的ETags。反而,他们将收到一个携带着组件所有数据的200正常响应。如果站点部署在单一服务器上,就根本不存在这个问题。但如果站点部署在多个服务器上,而且打算用Apache或者IIS的默认ETags配置,用户将看到缓慢的页面,服务器负载更高,还会消耗更大的带宽,并且代理也无法有效缓存页面内容。即使组件有“永久” Expires HTTP头,用户点击重新加载或者刷新的时候,仍然会发出条件GET请求。

如果不想用ETags提供的灵活的验证模型,最好把所有的Etag全都去掉,可以用基于组件的时间戳的 Last-Modified HTTP头验证,而且去掉ETag可以减少HTTP响应头以及后续请求的大小。 Microsoft Support article 里写了怎样移除ETags。在Apache中,可以简单地通过在Apache配置文件中添上如下代码来实现:

FileETag none

14.Ajax可缓存

分类: 内容

Ajax的一个好处是可以给用户提供即时反馈,因为它能够从后台服务器异步请求信息。然而,用了Ajax就无法保证用户在等待异步JavaScriptXML响应返回期间不会非常无聊。在很多应用程序中,用户能够一直等待取决于如何使用Ajax。例如,在基于web的电子邮件客户端中,用户为了寻找符合他们搜索标准的邮件消息,将会保持对Ajax请求返回结果的关注。重要的是,要记得“异步”并不意味着“即时”。

要提高性能,优化这些Ajax响应至关重要。最重要的提高Ajax性能的方法就是让响应变得可缓存,就像在 添上Expires或者Cache-Control HTTP头 中讨论的一样。下面适用于Ajax的其它规则:

Gzip组件

减少DNS查找

压缩JavaScript

避免重定向

配置ETags

我们一起看看例子,一个Web 2.0的电子邮件客户端用了Ajax来下载用户的通讯录,以便实现自动完成功能。如果用户从上一次使用之后再没有修改过她的通讯录,而且Ajax响应是可缓存的,有尚未过期的Expires或者Cache-Control HTTP头,那么之前的通讯录就可以从缓存中读出。必须通知浏览器,应该继续使用之前缓存的通讯录响应,还是去请求一个新的。可以通过给通讯录的Ajax URL里添加一个表明用户通讯录最后修改时间的时间戳来实现,例如 &t=1190241612 。如果通讯录从上一次下载之后再没有被修改过,时间戳不变,通讯录就将从浏览器缓存中直接读出,从而避免一次额外的HTTP往返消耗。如果用户已经修改了通讯录,时间戳也可以确保新的URL不会匹配缓存的响应,浏览器将请求新的通讯录条目。

即使Ajax响应是动态创建的,而且可能只适用于单用户,它们也可以被缓存,而这样会让你的Web 2.0应用更快。

15.尽早清空缓冲区

分类: 服务器

当用户请求一个页面时,服务器需要用大约200500毫秒来组装HTML页面,在这期间,浏览器闲等着数据到达。PHP中有一个 flush() 函数,允许给浏览器发送一部分已经准备完毕的HTML响应,以便浏览器可以在后台准备剩余部分的同时开始获取组件,好处主要体现在很忙的后台或者很“轻”的前端页面上(P.S. 也就是说,响应时耗主要在后台方面时最能体现优势)。

比较理想的清空缓冲区的位置是HEAD后面,因为HTMLHEAD部分通常更容易生成,并且允许引入任何CSSJavaScript文件,这样就可以让浏览器在后台还在处理的时候就开始并行获取组件。

例如:

... <!-- css, js -->
    </head>
    <?php flush(); ?>
    <body>
      ... <!-- content -->
登录后复制

Yahoo!搜索 开创了这项技术,而且真实用户测试研究也证明了使用这种技术的诸多好处。

16.AjaxGET请求

分类: 服务器

Yahoo!邮箱 团队发现使用 XMLHttpRequest 时,浏览器的POST请求是通过一个两步的过程来实现的:先发送HTTP头,在发送数据。所以最好用GET请求,它只需要发送一个TCP报文(除非cookie特别多)。IEURL长度最大值是2K,所以如果要发送的数据超过2K就无法使用GET了。

POST请求的一个有趣的副作用是实际上没有发送任何数据,就像GET请求一样。正如 HTTP说明文档 中描述的,GET请求是用来检索信息的。所以它的语义只是用GET请求来请求数据,而不是用来发送需要存储到服务器的数据。

17.延迟加载组件

分类: 内容

可以凑近看看页面并问自己:什么才是一开始渲染页面所必须的?其余内容都可以等会儿。

JavaScript是分隔onload事件之前和之后的一个理想选择。例如,如果有JavaScript代码和支持拖放以及动画的库,这些都可以先等会儿,因为拖放元素是在页面最初渲染之后的。其它可以延迟加载的部分包括隐藏内容(在某个交互动作之后才出现的内容)和折叠的图片。

工具可帮你减轻工作量: YUI Image Loader 可以延迟加载折叠的图片,还有 YUI Get utility 是一种引入JSCSS的简单方法。 Yahoo!主页 就是一个例子,可以打开Firebug的网络面板仔细看看。

最好让性能目标符合其它web开发最佳实践,比如“渐进增强”。如果客户端支持JavaScript,可以提高用户体验,但必须确保页面在不支持JavaScript时也能正常工作。所以,在确定页面运行正常之后,可以用一些延迟加载脚本增强它,以支持一些拖放和动画之类的华丽效果。

18.预加载组件

分类: 内容

预加载可能看起来和延迟加载是相反的,但它其实有不同的目标。通过预加载组件可以充分利用浏览器空闲的时间来请求将来会用到的组件(图片,样式和脚本)。用户访问下一页的时候,大部分组件都已经在缓存里了,所以在用户看来页面会加载得更快。

实际应用中有以下几种预加载的类型:

无条件 预加载——尽快开始加载,获取一些额外的组件。google.com就是一个sprite图片预加载的好例子,这个sprite图片并不是google.com主页需要的,而是搜索结果页面上的内容。

条件性 预加载——根据用户操作猜测用户将要跳转到哪里并据此预加载。在 search.yahoo.com 的输入框里键入内容后,可以看到那些额外组件是怎样请求加载的。

提前 预加载——在推出新设计之前预加载。经常在重新设计之后会听到:“这个新网站不错,但比以前更慢了”,一部分原因是用户访问先前的页面都是有旧缓存的,但新的却是一种空缓存状态下的体验。可以通过在将要推出新设计之前预加载一些组件来减轻这种负面影响,老站可以利用浏览器空闲的时间来请求那些新站需要的图片和脚本。

19.减少DOM元素的数量

分类: 内容

一个复杂的页面意味着要下载更多的字节,而且用JavaScript访问DOM也会更慢。举个例子,想要添加一个事件处理器的时候,循环遍历页面上的500DOM元素和5000DOM元素是有区别的。

大量的DOM元素是一种征兆——页面上有些内容无关的标记需要清理。正在用嵌套表格来布局吗?还是为了修复布局问题而添了一堆的

s?或许应该用更好的语义化标记。

YUI CSS utilities 对布局有很大帮助:grids.css针对整体布局,fonts.cssreset.css可以用来去除浏览器的默认格式。这是个开始清理和思考标记的好机会,例如只在语义上有意义的时候使用

,而不是因为它能够渲染一个新行。

DOM元素的数量很容易测试,只需要在Firebug的控制台里输入:

document.getElementsByTagName('*').length

那么多少DOM元素才算是太多呢?可以参考其它类似的标记良好的页面,例如 Yahoo!主页 是一个相当繁忙的页面,但只有不到700个元素(HTML标签)。

20.跨域分离组件

分类: 内容

分离组件可以最大化并行下载,但要确保只用不超过2-4个域,因为存在DNS查找的代价。例如,可以把HTML和动态内容部署在 www.example.org ,而把静态组件分离到 static1.example.org static2.example.org

更多信息请查看Tenni TheurerPatty Chi的文章: Maximizing Parallel Downloads in the Carpool Lane

21.尽量少用iframe

分类: 内容

iframe可以把一个HTML文档插入到父文档里,重要的是明白iframe是如何工作的并高效地使用它。