咨询:13529513104

写在前面

英文原版链接 ,若是觉得本文哪里不好还请指出,以便及时修改

目录(分7类,共35条):

  1. [内容]尽量减少HTTP请求数

  2. [服务器]使用CDN(Content Delivery Network)

  3. [服务器]添上Expires或者Cache-Control HTTP头

  4. [服务器]Gzip组件

  5. [css]把样式表放在顶部

  6. [js]把脚本放在底部

  7. [css]避免使用CSS表达式

  8. [js, css]把JavaScript和CSS放到外面

  9. [内容]减少DNS查找

  10. [js, css]压缩JavaScript和CSS

  11. [内容]避免重定向

  12. [js]去除重复脚本

  13. [服务器]配置ETags

  14. [内容]让Ajax可缓存

  15. [服务器]尽早清空缓冲区

  16. [服务器]对Ajax用GET请求

  17. [内容]延迟加载组件

  18. [内容]预加载组件

  19. [内容]减少DOM元素的数量

  20. [内容]跨域分离组件

  21. [内容]尽量少用iframe

  22. [内容]杜绝404

  23. [cookie]给Cookie减肥

  24. [cookie]把组件放在不含cookie的域下

  25. [js]尽量减少DOM访问

  26. [js]用智能的事件处理器

  27. [css]选择 舍弃@import

  28. [css]避免使用滤镜

  29. [图片]优化图片

  30. [图片]优化CSS Sprite

  31. [图片]不要用HTML缩放图片

  32. [图片]用小的可缓存的favicon.ico(P.S. 收藏夹图标)

  33. [移动端]保证所有组件都小于25K

  34. [移动端]把组件打包到一个复合文档里

  35. [服务器]避免图片src属性为空

译文:给网站提速的最佳实践

我们已经发现了不少给站点提速的最佳实践,分7类共35条。

1.尽量减少HTTP请求数

分类: 内容

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

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

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

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

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

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

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

2.使用CDN(Content Delivery Network)

分类: 服务器

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

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

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

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

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

3.添上Expires或者Cache-Control HTTP头

分类: 服务器

这条规则有两个方面:

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

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

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

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

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

如果你用的是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.3用 mod_gzip 而Apache 2.x是 mod_deflate 模块。

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

服务器会根据文件类型来决定要不要用gzip压缩,但这非常有限。大多数网站都用gzip压缩HTML文件,其实压缩脚本,样式表也是不错的选择,但很多网站却错失了这个机会。其实,可以压缩任何文本内容,包括XML和JSON,而图片和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表达式把背景颜色设置成按小时交替的:

background-color: expression( (newDate()).getHours()%2 ? "#B8D4FF" : "#F08A00" );

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

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

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

8.把JavaScript和CSS放到外面

分类: javascript, css

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

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

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

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

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

9.减少DNS查找

分类: 内容

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

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

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

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

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

10.压缩JavaScript和CSS

分类: javascript, css

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

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

除了压缩外部脚本和样式,行内的 <script><style> 块也可以压缩。即使启用了gzip模块,先进行压缩也能够缩小5%或者更多的大小。JavaScript和CSS的用处越来越多,所以压缩代码会有不错的效果。

11.避免重定向

分类: 内容

重定向用301和302状态码,下面是一个有301状态码的HTTP头:

HTTP/1.1 301 Moved PermanentlyLocation: http://example.com/newuriContent-Type: text/html

浏览器会自动跳转到 Location 域指明的URL。重定向需要的所有信息都在HTTP头部,而响应体一般是空的。其实额外的HTTP头,比如 ExpiresCache-Control 也表示重定向。除此之外还有别的跳转方式:refresh元标签和JavaScript,但如果你必须得做重定向,最好用标准的3xxHTTP状态码,主要是为了让返回按钮能正常使用。

牢记重定向会拖慢用户体验,在用户和HTML文档之间插入重定向会延迟页面上的所有东西,页面无法渲染,组件也无法开始下载,直到HTML文档被送达浏览器。

有一种常见的极其浪费资源的重定向,而且web开发人员一般都意识不到这一点,就是URL尾部缺少一个斜线的时候。例如,跳转到 http://astrology.yahoo.com/astrology 会返回一个重定向到 http://astrology.yahoo.com/astrology/ 的301响应(注意添在尾部的斜线)。在Apache中可以用 Aliasmod_rewrite 或者 DirectorySlash 指令来取消不必要的重定向。

重定向最常见的用途是把旧站点连接到新的站点,还可以连接同一站点的不同部分,针对用户的不同情况(浏览器类型,用户帐号类型等等)做一些处理。用重定向来连接两个网站是最简单的,只需要少量的额外代码。虽然在这些时候使用重定向减少了开发人员的开发复杂度,但降低了用户体验。一种替代方案是用 Aliasmod_rewrite ,前提是两个代码路径都在相同的服务器上。如果是因为域名变化而使用了重定向,就可以创建一条CNAME(创建一个指向另一个域名的DNS记录作为别名)结合Alias 或者 mod_rewrite 指令。

12.去除重复脚本

分类: javascript

页面含有重复的脚本文件会影响性能,这可能和你想象的不一样。在对美国前10大web站点的评审中,发现只有2个站点含有重复脚本。两个主要原因增加了在单一页面中出现重复脚本的几率:团队大小和脚本数量。在这种情况下,重复脚本会创建不必要的HTTP请求,执行无用的JavaScript代码,而影响页面性能。

IE会产生不必要的HTTP请求,而Firefox不会。在IE中,如果一个不可缓存的外部脚本被页面引入了两次,它会在页面加载时产生两个HTTP请求。即使脚本是可缓存的,在用户重新加载页面时也会产生额外的HTTP请求。

除了产生没有意义的HTTP请求之外,多次对脚本求值也会浪费时间。因为无论脚本是否可缓存,在Firefox和IE中都会执行冗余的JavaScript代码。

避免不小心把相同脚本引入两次的一种方法就是在模版系统中实现脚本管理模块。典型的脚本引入方法就是在HTML页面中用SCRIPT标签:

<scripttype="text/javascript"src="menu_1.0.17.js"></script>

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


		
【责任编辑:(Top) 返回页面顶端