本文是大名鼎鼎的雅虎前端优化规则(Yslow)的翻译。翻译并不逐字逐句,部分难以逐字翻译的被意译了,另一些不重要的举例等也被精简。php
原文: Best Practices for Speeding Up Your Web Site。css
如何让web页面更快,雅虎团队实践总结了7类35条规则,下面一一列出。html
Minimize HTTP Requests减小/最小化 http 请求数。前端
到终端用户的响应时间80%花在前端:大部分用于下载组件(js/css/image/flash等等)。减小组件数就是减小渲染页面所需的http请求数。这是更快页面的关键。html5
减小组件数的一个方法就是简化页面设计。保持富内容的页面且能减小http请求,有如下几个技术:web
background-image
和 background-position
来显示不一样部分。data:url scheme
来內连图片。减小请求数是为第一次访问页面的用户提升性能的最重要的指导。ajax
减小DNS查询。express
就像电话簿,你在浏览器地址栏输入网址,经过DNS查询获得网站真实IP。json
DNS查询被缓存来提升性能。这种缓存可能发生在特定的缓存服务器(ISP/local area network维护),或者用户的计算机。DNS信息留存在操做系统DNS缓存中(在windows中就是 DNS Client Serve )。大多浏览器有本身的缓存,独立于操做系统缓存。只要浏览器在本身的缓存里有某条DNS记录,它就不会向操做系统发DNS解析请求。windows
IE默认缓存DNS记录30分钟,FireFox默认缓存1分钟。
当客户端的DNS缓存是空的,DNS查找次数等于页面中的惟一域名数。
减小DNS请求数可能会减小并行下载数。避免DNS查找减小响应时间,但减小并行下载数可能会增长响应时间。指导原则是组件能够分散在至少2个但很少于4个的不一样域名。这是二者的妥协。
避免跳转。
跳转用301
或302
状态码来达成。一个301
响应http头的例子:
HTTP/1.1 301 Moved Permanently Location: http://example.com/newuri Content-Type: text/html
浏览器自动跳转到Location
指定的路径。跳转所需的全部信息都在http头,因此http主体通常是空的。301
302
响应通常不会被缓存,除非有额外的头部信息,好比Expires
或Cache-Control
指定要缓存。meta
刷新标签或 JavaScript 也能够跳转,但若是真要跳转,3xx
跳转更好,主要是保证返回键可用。
跳转显然拖慢响应速度。在跳转的页面被获取前浏览器没什么能渲染,没什么组件能下载。
最浪费的跳转之一发生在url尾部slash(/)缺失。好比http://astrology.yahoo.com/astrology
会301
跳转到http://astrology.yahoo.com/astrology/
。这能够被Apache等服务器修复,用Alias
,mod_rewrite
等等。
让Ajax可缓存。
使用ajax的好处是能够向用户提供很快的反馈,由于它是向后台异步请求数据。可是,这些异步请求不保证用户等待的时间——异步不意味着瞬时。
提升ajax性能的最重要的方法是让响应被缓存,即在Add an Expires or a Cache-Control Header中讨论的 Expires 。其它方法是:
延迟加载组件。
再看看你的页面而后问问本身,“什么是页面初始化必须的?”。剩下的内容和组件能够延迟。
JavaScript是理想的(延迟)候选者,能够切分到onload
事件以前和以后。好比拖放的js库能够延迟,由于拖动必须在页面初始化以后。其它可延迟的包括隐藏的内容,折叠起来的图片等等。
预加载组件。
预加载看起来与延迟加载相反,但它的确有个不一样的目标。经过预加载你能够利用浏览器的空闲时间来请求你未来会用到的组件。这样当用户访问下一个页面时,你会有更多的组件已经在缓存中,这样会极大加快页面加载。
有几种预加载类型:
onload
触发,你当即获取另外的组件。好比谷歌会在主页这样加载搜索结果页面用到的雪碧图。减小dom数。
一个复杂的页面意味着更多的内容要下载,以及更慢的dom访问。好比在有500dom数量的页面添加事件处理就和有5000dom数量的不一样。
若是你的页面dom元素不少,那么意味着你可能须要删除无用的内容和标签来优化。
把组件分散到不一样的域名。
把组件分散到不一样的域名容许你最大化并行下载数。因为DNS查询的反作用,最佳的不一样域名数是2-4。
最小化iframe的数量。
iframe容许html文档被插入到父文档。
<iframe>
优势:
<iframe>
缺点:
onload
不要404。
http请求是昂贵的,因此发出http请求但得到没用的响应(如404)是彻底没必要要的,而且会下降用户体验。
一些网站会有特别的404页面提升用户体验,但这仍然会浪费服务器资源。特别坏的是当连接指向外部js但却获得404结果。这样首先会下降(占用)并行下载数,其次浏览器可能会把404响应体看成js来解析,试图从里面找出可用的东西。
使用CDN。
用户接近你的服务器会减小响应时间。把你的内容发布到多个,地理上分散的服务器可让页面加载更快。但怎么开始?
首先不要试图把你的架构从新设计成分布式架构。由于可能引进更多复杂性和不可控。
记住80-90%的终端用户响应时间花费在下载页面中的全部组件:图片、样式、脚本、falsh等等。这是_Performance Golden Rule_。不要从困难的从新设计后台架构开始,最好首先分发你的静态内容。这不只能够减小响应时间,用CDN还很容易来作。
CDN是一群不一样地点的服务器,能够更高效地分发内容到用户。一些大公司有本身的CDN。
加Expires
或者Cache-Control
头部。
这条规则有两个方面:
Expires
头部来实现“永不过时”策略。Cache-Control
头部来帮助浏览器进行有条件请求。页面愈来愈丰富,意味着更多脚本,样式,图片等等。第一次访问的用户可能须要发出多个请求,但使用Expires可让这些组件被缓存。这避免了访问子页面时不必的http请求。Expires通常用在图片上,但应该用在全部的组件上。
浏览器(以及代理)使用缓存来减小http请求数,加快页面加载。服务器使用http响应的Expires
头部来告诉客户端一个组件能够缓存多久。好比下面:
Expires: Thu, 15 Apr 2010 20:00:00 GMT //2010-04-15以前都是稳定的
注意,若是你设置了Expires
头部,当组件更新后,你必须更改文件名。
传输时用gzip等压缩组件。
http请求或响应的传输时间能够被前端工程师显著减小。终端用户的带宽,ISP,接近对等交换点等等无法被开发团队控制,可是,压缩能够经过减小http响应的大小减小响应时间。
从HTTP/1.1
开始,客户端经过http请求中的Accept-Encoding
头部来提示支持的压缩:
Accept-Encoding: gzip, deflate
若是服务器看到这个头部,它可能会选用列表中的某个方法压缩响应。服务器经过Content-Encoding
头部提示客户端:
Content-Encoding: gzip
gzip通常可减少响应的70%。尽量去gzip更多(文本)类型的文件。html,脚本,样式,xml和json等等都应该被gzip,而图片,pdf等等不该该被gzip,由于它们自己已被压缩过,gzip它们只是浪费cpu,甚至增长文件大小。
实体标记(Entity tags,ETag)是服务器和浏览器之间判断浏览器缓存中某个组件是否匹配服务器端原组件的一种机制。实体就是组件:图片,脚本,样式等等。ETag被看成验证明体的比最后更改(last-modified
)日期更高效的机制。服务器这样设置组件的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给服务器。若是ETag匹配,服务器返回304:
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
ETag的问题是它们被构造来使它们对特定的运行这个网站的服务器惟一。浏览器从一个服务器获取组件,以后向另外一个服务器验证,ETag将不匹配。然而服务器集群是处理请求的通用解决方案。
若是不能解决多服务器间的ETag匹配问题,那么删除ETag可能更好。
早一点刷新buffer(尽早给浏览器数据)。
当用户请求一个页面,服务器通常要花200-500ms来拼凑整个页面。这段时间,浏览器是空闲的(等数据返回)。在php,有个方法flush()
容许你传输部分准备好的html响应给浏览器。这样的话浏览器就能够开始下载组件,而同时后台能够继续生成页面剩下的部分。这种好处更可能是在忙碌的后台或轻前端网站能够看到。
一个比较好的flush的位置是在head
以后,由于浏览器能够加载其中的样式和脚本文件,然后台继续生成页面剩余部分。
<!-- css, js --> </head> <?php flush(); ?> <body> <!-- content -->
ajax请求用get。
Yahoo! Mail团队发现当使用XMLHttpRequest
,POST 被浏览器实现为两步:首先发送头部,而后发送数据。因此使用GET最好,仅用一个TCP包发送(除非cookie太多)。IE的url长度限制是2K。
POST但不提交任何数据根GET行为相似,但从语义上讲,获取数据应该用GET,提交数据到服务器用POST。
避免空src的图片。
空src属性的图片的行为可能跟你预期的不同。它有两种形式:
<img src="">
var img = new Image(); img.src = "";
两种都会形成同一种后果:浏览器会向你的服务器发请求。
为何这种行为很糟糕?
这种行为的根源是uri解析发生在浏览器。RFC 3986 定义了这种行为,空字符串被看成相对路径,Firefox, Safari, 和 Chrome都正确解析,而IE错误。总之,浏览器解析空字符串为相对路径的行为被认为是符合预期的。
html5在_4.8.2_添加了对标签src属性的描述,指导浏览器不要发出额外的请求。
The src attribute must be present, and must contain a valid URL referencing a non-interactive, optionally animated, image resource that is neither paged nor scripted. If the base URI of the element is the same as the document's address, then the src attribute's value must not be the empty string.
幸运的是未来浏览器不会有这个问题了(在图片上)。不幸的是,<script src="">
和<link href="">
没有这样的规范。
http cookie的使用有多种缘由,好比受权和个性化。cookie的信息经过http头部在浏览器和服务器端交换。尽量减少cookie的大小来下降响应时间。
用没有cookie的域名提供组件。
当浏览器请求静态图片并把cookie一块儿发送到服务器时,cookie此时对服务器没什么用处。因此这些cookie只是增长了网络流量。因此你应该保证静态组件的请求是没有cookie的。能够建立一个子域名来托管全部静态组件。
好比,你域名是www.example.org
,能够把静态组件托管在static.example.org
。不过,你若是把cookie设置在顶级域名example.org
下,这些cookie仍然会被传给static.example.org
。这种状况下,启用一个全新的域名来托管静态组件。
另一个用没有cookie的域名提供组件的好处是,某些代理可能会阻止缓存待cookie的静态组件请求。
把样式放在顶部。
研究雅虎网页性能时发现把样式表移到<head>
里会让页面更快。这是由于把样式表移到<head>
里容许页面逐步渲染。
关注性能的前端工程师但愿页面被逐步渲染,这时由于,咱们但愿浏览器尽早渲染获取到的任何内容。这对大页面和网速慢的用户很重要。给用户视觉反馈,好比进度条的重要性已经被大量研究和记录。在咱们的状况中,HTML
页面就是进度条。当浏览器逐步加载页面头部,导航条,logo等等,这些都是给等待页面的用户的视觉反馈。这优化了总体用户体验。
把样式表放在文档底部的问题是它阻止了许多浏览器的逐步渲染,包括IE。这些浏览器阻止渲染来避免在样式更改时须要重绘页面元素。因此用户会卡在白屏。
HTML规范清楚代表样式应该在<head>
里。
避免CSS表达式。
CSS表达式是强大的(可能也是危险的)设置动态CSS属性的方法。IE5开始支持,IE8开始不同意使用。例如,背景颜色能够设置成每小时轮换:
background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );
CSS表达式的问题是它们可能比大多数人预期的计算的更频繁。它们不只在页面载入和调整大小时从新计算,也在滚动页面甚至是用户在页面上移动鼠标时计算。好比在页面上移动鼠标可能轻易计算超过10000次。
要避免CSS表达式计算太屡次,能够在它第一次计算后替换成确切值,或者用事件处理函数而不是CSS表达式。
<link>
over @import
选择<link>
而不是@import
。
以前的一个最佳原则是说CSS应该在顶部来容许逐步渲染。
在IE用@import
和把CSS放到页面底部行为一致,因此最好别用。
避免使用(IE)过滤器。
IE专有的AlphaImageLoader
过滤器用于修复IE7如下版本的半透明真彩色PNG的问题。这个过滤器的问题是它阻止了渲染,并在图片下载时冻结了浏览器。另外它还引发内存消耗,而且它被应用到每一个元素而不是每一个图片,因此问题(的严重性)翻倍了。
最佳作法是放弃AlphaImageLoader
,改用PNG8来优雅降级。
把脚本放到底部。
脚本引发的问题是它们阻塞了并行下载。HTTP1.1规范建议浏览器每一个域名下不要一次下载超过2个组件。若是你的图片分散在不一样服务器,那么你能并行下载多个图片。但当脚本在下载,浏览器不会再下载其它组件,即便在不一样域名下。
有些状况下把脚本移动到底部并不简单。好比,脚本中用了document.write
来插入内容,它就不能被移动到底部。另外有可能有做用域问题。但大多数状况,有方法能够解决这些问题。
一个替代建议是使用异步脚本。defer
属性代表脚本不包含document.write
,是提示浏览器继续渲染的线索。不幸的是,Firefox不支持。若是脚本能异步,那么也就能够移动到底部。
使用外部JS和CSS。
这里的不少性能规则涉及外部组件怎么管理。但你首先要明白一个基本问题:JS和CSS是应该包含在外部文件仍是內连在页面自己?
真实世界中使用外部文件通常会加快页面,由于JS和CSS文件被浏览器缓存了。內连的JS和CSS怎在每次HTML文档下载时都被下载。內连减小了http请求,但增长了HTML文档大小。另外一方面,若是JS和CSS被缓存了,那么HTML文档能够减少大小而不增长HTTP请求。
核心因素,就是JS和CSS被缓存相对于HTML文档被请求的频率。尽管这个因素很难被量化,但能够用不一样的指标来计算。若是网站用户每一个session有多个pv,许多页面重用相同的JS和CSS,那么有很大可能用外部JS和CSS更好。
许多网站用这些指标计算后在中间位置。对这些网站来讲,最佳方案仍是用外部JS和CSS文件。惟一例外是內连更被主页偏心,如http://www.yahoo.com/。主页每一个session可能只有少许的甚至一个pv,这时候內连可能更快。
对多个页面的首页来讲,能够经过技术减小(其它页面的)http请求。在首页用內连,初始化后动态加载外部文件,接下来的页面若是用到这些文件,就可使用缓存了。
压缩JS和CSS。
压缩就是删除代码中没必要要的字符来减少文件大小,从而提升加载速度。当代码压缩时,注释删除,不须要的空格(空白,换行,tab)也被删除。
混淆是对代码可选的优化。它比压缩更复杂,而且可能产生bug。在对美国top10网站的调查,压缩可减少21%,而混淆可减少25%。
除了外部脚本和样式,內连的脚本和样式一样应该被压缩。
删除重复的脚本。
在页面中引入相同的脚本两次会伤害性能。可能超出你的预料,美国top10网站的2家有重复脚本引入。两个主要因素形成同一页面引入相同脚本:团队大小和脚本数量。当确实引入重复脚本,会发出没必要要的http请求和浪费js执行时间。
发出没必要要的http请求发生在IE而不是Firefox。在IE,若是外部脚本引入两次且没有缓存,它会发出2个请求。即便脚本被缓存,刷新时也会发出额外请求。
除了增长http请求,时间被浪费在执行脚本屡次上。无论IE仍是Firefox都会执行屡次。
一种避免屡次引入脚本的方法是在模板系统实现一个脚本管理模块。
最小化DOM访问。
用JS访问DOM元素是缓慢的,因此为了响应更好的页面,你应该:
开发聪明的事件处理
有时候页面看起来不那么响应(响应速度慢),是由于绑定到不一样元素的大量事件处理函数执行太屡次。这是为何使用_事件委托_是一种好方法。
另外,你没必要等到onload
事件来开始处理DOM树,DOMContentLoaded
更快。大多时候你须要的只是想访问的元素已在DOM树中,因此你没必要等到全部图片被下载。
优化图片
在设计师建好图片后,在上传图片到服务器前你仍能够作些事:
pngcrush
或其它工具压缩png。jpegtran
或其它工具压缩jpeg。优化CSS雪碧图
不要在html中缩放图片
不要由于你能够设置图片的宽高就去用比你须要的大得多的图片。若是你须要
<img width="100" height="100" src="mycat.jpg" alt="My Cat" />
那么,就用100x100px的图片,而不是500x500px的。
favicon.ico小且缓存
favicon.ico是在你服务器根路径的图片。邪恶的是即便你不关心它,浏览器仍然会请求它。因此最好不要响应404。另外因为在同一服务器,每次请求favicon.ico时也会带上cookie。这个图片还会影响下载顺序,好比在IE,若是你在onload
时下载额外的组件,fcvicon会在这些组件以前被下载。
怎么减轻favicon.ico的缺点?
保持组件小于25K
这个限制与iPhone不缓存大于25K的组件相关。注意,这是非压缩(uncompressed)的文件大小。在这里minification(压缩,不要与compress混淆)很重要,由于gzip没法知足(iPhone)。
打包组件到一个多部父文档
打包组件到一个多部父文档相似于带附件的邮件。它帮助你在一个http请求中获取多个组件,但注意,iPhone不支持。
浏览器刷新是conditional request
,因此若是经过刷新来看缓存是否有效确定是304。能够试试输入网址按回车或者回退键来看效果。另外因为HTML文档不多设置彻底缓存(通常要和服务器验证),能够看静态组件的缓存效果(200 ok (from cache)
)。
expirationTime = responseTime + freshnessLifetime - currentAge
freshnessLifetime
具体怎么算能够参考https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching_FAQ。
Flash of unstyled content(FOUC)就是在加载外部样式表以前,浏览器按默认样式显示了内容,这是由于浏览器在全部资源都下载好前就开始渲染页面了。一旦外部样式被加载,浏览器就会修正样式,但这种修正多是可见的,也就是FOUC。
怎么避免?在<head>
中经过<link>
引入样式,避免使用@import
。