合并文件是经过把全部脚本放在一个文件中的方式来减小请求数的,固然,也能够合并全部的CSS。若是各个页面的脚本和样式不同的话,合并文件就是一项比较麻烦的工做了,但把这个做为站点发布过程的一部分确实能够提升响应时间。javascript
CSS Sprites是减小图片请求数量的首选方式。把背景图片都整合到一张图片中,而后用CSS的background-image
和background-position
属性来定位要显示的部分。php
图像映射能够把多张图片合并成单张图片,总大小是同样的,但减小了请求数并加速了页面加载。图片映射只有在图像在页面中连续的时候才有用,好比导航条。给image map设置坐标的过程既无聊又容易出错,用image map来作导航也不容易,因此不推荐用这种方式。css
行内图片(Base64编码)用data:
URL模式来把图片嵌入页面。这样会增长HTML文件的大小,把行内图片放在(缓存的)样式表中是个好办法,并且成功避免了页面变“重”。但目前主流浏览器并不能很好地支持行内图片。html
减小页面的HTTP请求数是个起点,这是提高站点首次访问速度的重要指导原则。前端
域名系统创建了主机名和IP地址间的映射,就像电话簿上人名和号码的映射同样。当你在浏览器输入www.yahoo.com的时候,浏览器就会联系DNS解析器返回服务器的IP地址。DNS是有成本的,它须要20到120毫秒去查找给定主机名的IP地址。在DNS查找完成以前,浏览器没法从主机名下载任何东西。java
重定向用301和302状态码,牢记重定向会拖慢用户体验,在用户和HTML文档之间插入重定向会延迟页面上的全部东西,页面没法渲染,组件也没法开始下载,直到HTML文档被送达浏览器。web
若是用户从上一次使用以后再没有修改过她的通信录,并且Ajax响应是可缓存的,有还没有过时的Expires或者Cache-Control HTTP头,那么以前的通信录就能够从缓存中读出。必须通知浏览器,应该继续使用以前缓存的通信录响应,仍是去请求一个新的。能够经过给通信录的Ajax URL里添加一个代表用户通信录最后修改时间的时间戳来实现,例如&t=1190241612
。若是通信录从上一次下载以后再没有被修改过,时间戳不变,通信录就将从浏览器缓存中直接读出,从而避免一次额外的HTTP往返消耗。若是用户已经修改了通信录,时间戳也能够确保新的URL不会匹配缓存的响应,浏览器将请求新的通信录条目。数据库
即便Ajax响应是动态建立的,并且可能只适用于单用户,它们也能够被缓存,而这样会让你的Web 2.0应用更快。express
一个复杂的页面意味着要下载更多的字节,并且用JavaScript访问DOM也会更慢。举个例子,想要添加一个事件处理器的时候,循环遍历页面上的500个DOM元素和5000个DOM元素是有区别的。api
grids.css针对总体布局,fonts.css和reset.css能够用来去除浏览器的默认格式。这是个开始清理和思考标记的好机会,例如只在语义上有意义的时候使用<div>
,而不是由于它可以渲染一个新行。
DOM元素的数量很容易测试,只须要在控制台里输入:document.getElementsByTagName('*').length
用iframe能够把一个HTML文档插入到父文档里,重要的是明白iframe是如何工做的并高效地使用它。
<iframe>
的优势:
<iframe>
的缺点:
HTTP请求代价高昂,彻底没有必要用一个HTTP请求去获取一个无用的响应(好比404 Not Found),只会拖慢用户体验而没有任何好处。
用CSS表达式动态设置CSS属性,是一种强大又危险的方式。从IE5开始支持,但从IE8起就不推荐使用了。例如,能够用CSS表达式把背景颜色设置成按小时交替的:
1
|
background-color
: expression( (new Date()).getHours()%
2
?
"#B8D4FF"
:
"#F08A00"
);
|
前面提到了一个最佳实践:为了实现逐步渲染,CSS应该放在顶部。
在IE中用@import
与在底部用<link>
效果同样,因此最好不要用它。
IE专有的AlphaImageLoader
滤镜能够用来修复IE7以前的版本中半透明PNG图片的问题。在图片加载过程当中,这个滤镜会阻塞渲染,卡住浏览器,还会增长内存消耗并且是被应用到每一个元素的,而不是每一个图片,因此会存在一大堆问题。
最好的方法是干脆不要用AlphaImageLoader
,而优雅地降级到用在IE中支持性很好的PNG8图片来代替。若是非要用AlphaImageLoader
,应该用下划线hack:_filter
来避免影响IE7及更高版本的用户。
在Yahoo!研究性能的时候,咱们发现把样式表放到文档的HEAD部分能让页面看起来加载地更快。这是由于把样式表放在head里能让页面逐步渲染。
关注性能的前端工程师想让页面逐步渲染。也就是说,咱们想让浏览器尽快显示已有内容,这在页面上有一大堆内容或者用户网速很慢时显得尤其重要。给用户显示反馈(好比进度指标)的重要性已经被普遍研究过,而且被记录下来了。在咱们的例子中,HTML页面就是进度指标!当浏览器逐渐加载页面头部,导航条,顶部logo等等内容的时候,这些都被正在等待页面加载的用户看成反馈,可以提升总体用户体验。
避免不当心把相同脚本引入两次的一种方法就是在模版系统中实现脚本管理模块。典型的脚本引入方法就是在HTML页面中用SCRIPT标签:
1
|
<
script
type="text/javascript" src="menu_1.0.17.js"></
script
>
|
用JavaScript访问DOM元素是很慢的,因此,为了让页面反应更迅速,应该:
有时候感受页面反映不够灵敏,是由于有太多频繁执行的事件处理器被添加到了DOM树的不一样元素上,这就是推荐使用事件委托的缘由。若是一个div
里面有10个按钮,应该只给div容器添加一个事件处理器,而不是给每一个按钮都添加一个。事件可以冒泡,因此能够捕获事件并得知哪一个按钮是事件源。
实际上,用外部文件可让页面更快,由于JavaScript和CSS文件会被缓存在浏览器。HTML文档中的行内JavaScript和CSS在每次请求该HTML文档的时候都会从新下载。这样作减小了所需的HTTP请求数,但增长了HTML文档的大小。另外一方面,若是JavaScript和CSS在外部文件中,而且已经被浏览器缓存起来了,那么咱们就成功地把HTML文档变小了,并且尚未增长HTTP请求数。
压缩具体来讲就是从代码中去除没必要要的字符以减小大小,从而提高加载速度。代码最小化就是去掉全部注释和没必要要的空白字符(空格,换行和tab)。在JavaScript中这样作可以提升响应性能,由于要下载的文件变小了。两个最经常使用的JavaScript代码压缩工具是JSMin和YUI Compressor,YUI compressor还能够压缩CSS。
混淆是一种可选的源码优化措施,要比压缩更复杂,因此混淆过程也更容易产生bug。在对美国前十的网站调查中,压缩能够缩小21%,而混淆能缩小25%。虽然混淆的缩小程度更高,但比压缩风险更大。
除了压缩外部脚本和样式,行内的<script>
和<style>
块也能够压缩。即便启用了gzip模块,先进行压缩也可以缩小5%或者更多的大小。JavaScript和CSS的用处愈来愈多,因此压缩代码会有不错的效果。
不要由于在HTML中能够设置宽高而使用本不须要的大图。若是须要
1
|
<
img
width="100" height="100" src="mycat.jpg" alt="My Cat" />
|
那么图片自己(mycat.jpg)应该是100x100px的,而不是去缩小500x500px的图片。
favicon.ico是放在服务器根目录的图片,它会带来一堆麻烦,由于即使你无论它,浏览器也会自动请求它,因此最好不要给一个404 Not Found
响应。并且只要在同一个服务器上,每次请求它时都会发送cookie,此外这个图片还会干扰下载顺序,例如在IE中,当你在onload中请求额外组件时,将会先下载favicon。
因此为了缓解favicon.ico的缺点,应该确保:
使用cookie的缘由有不少,好比受权和个性化。HTTP头中cookie信息在web服务器和浏览器之间交换。重要的是保证cookie尽量的小,以最小化对用户响应时间的影响。
当浏览器发送对静态图像的请求时,cookie也会一块儿发送,而服务器根本不须要这些cookie。因此它们只会形成没有意义的网络通讯量,应该确保对静态组件的请求不含cookie。能够建立一个子域,把全部的静态组件都部署在那儿。
若是域名是www.example.org
,能够把静态组件部署到static.example.org
。然而,若是已经在顶级域example.org
或者www.example.org
设置了cookie,那么全部对static.example.org
的请求都会含有这些cookie。这时候能够再买一个新域名,把全部的静态组件部署上去,并保持这个新域名不含cookie。Yahoo!用的是yimg.com
,YouTube是ytimg.com
,Amazon是images-amazon.com
等等。
把静态组件部署在不含cookie的域下还有一个好处是有些代理可能会拒绝缓存带cookie的组件。有一点须要注意:若是不知道应该用example.org仍是www.example.org做为主页,能够考虑一下cookie的影响。省略www的话,就只能把cookie写到*.example.org
,因此由于性能缘由最好用www子域,而且把cookie写到这个子域下。
这个限制是由于iPhone不能缓存大于25K的组件,注意这里指的是未压缩的大小。这就是为何缩减内容自己也很重要,由于单纯的gzip可能不够。
把各个组件打包成一个像有附件的电子邮件同样的复合文档里,能够用一个HTTP请求获取多个组件(记住一点:HTTP请求是代价高昂的)。用这种方式的时候,要先检查用户代理是否支持(iPhone就不支持)。
前端工程师能够想办法明显地缩短经过网络传输HTTP请求和响应的时间。毫无疑问,终端用户的带宽速度,网络服务商,对等交换点的距离等等,都是开发团队所没法控制的。但还有别的可以影响响应时间的因素,压缩能够经过减小HTTP响应的大小来缩短响应时间。
从HTTP/1.1开始,web客户端就有了支持压缩的Accept-Encoding HTTP请求头。
1
|
Accept-Encoding: gzip, deflate
|
若是web服务器看到这个请求头,它就会用客户端列出的一种方式来压缩响应。web服务器经过Content-Encoding相应头来通知客户端。
1
|
Content-Encoding: gzip
|
尽量多地用gzip压缩可以给页面减肥,这也是提高用户体验最简单的方法。
Image with empty string src属性是空字符串的图片很常见,主要以两种形式出现:
<img src=””>
var img = new Image();
img.src = “”;
这两种形式都会引发相同的问题:浏览器会向服务器发送另外一个请求。
Yahoo!邮箱团队发现使用XMLHttpRequest
时,浏览器的POST请求是经过一个两步的过程来实现的:先发送HTTP头,在发送数据。因此最好用GET请求,它只须要发送一个TCP报文(除非cookie特别多)。IE的URL长度最大值是2K,因此若是要发送的数据超过2K就没法使用GET了。
POST请求的一个有趣的反作用是实际上没有发送任何数据,就像GET请求同样。正如HTTP说明文档中描述的,GET请求是用来检索信息的。因此它的语义只是用GET请求来请求数据,而不是用来发送须要存储到服务器的数据。
当用户请求一个页面时,服务器须要用大约200到500毫秒来组装HTML页面,在这期间,浏览器闲等着数据到达。PHP中有一个flush()函数,容许给浏览器发送一部分已经准备完毕的HTML响应,以便浏览器能够在后台准备剩余部分的同时开始获取组件,好处主要体如今很忙的后台或者很“轻”的前端页面上(P.S. 也就是说,响应时耗主要在后台方面时最能体现优点)。
较理想的清空缓冲区的位置是HEAD后面,由于HTML的HEAD部分一般更容易生成,而且容许引入任何CSS和JavaScript文件,这样就可让浏览器在后台还在处理的时候就开始并行获取组件。
例如:
... <!-- css, js --> </head> <?php flush(); ?> <body> ... <!-- content -->
用户与服务器的物理距离对响应时间也有影响。把内容部署在多个地理位置分散的服务器上能让用户更快地载入页面。但具体要怎么作呢?
实现内容在地理位置上分散的第一步是:不要尝试去从新设计你的web应用程序来适应分布式结构。这取决于应用程序,改变结构可能包括一些让人望而生畏的任务,好比同步会话状态和跨服务器复制数据库事务(翻译可能不许确)。缩短用户和内容之间距离的提议可能被推迟,或者根本不可能经过,就是由于这个难题。
记住终端用户80%到90%的响应时间都花在下载页面组件上了:图片,样式,脚本,Flash等等,这是业绩黄金法则。最好先分散静态内容,而不是一开始就从新设计应用程序结构。这不只可以大大减小响应时间,还更容易表现出CDN的功劳。
内容分发网络(CDN)是一组分散在不一样地理位置的web服务器,用来给用户更高效地发送内容。典型地,选择用来发送内容的服务器是基于网络距离的衡量标准的。例如:选跳数(hop)最少的或者响应时间最快的服务器。
这条规则有两个方面:
Expires
来实现永不失效Cache-Control
HTTP头来让浏览器进行条件性的请求网页设计愈来愈丰富,这意味着页面里有更多的脚本,图片和Flash。站点的新访客可能仍是不得不提交几个HTTP请求,但经过使用有效期能让组件变得可缓存,这避免了在接下来的浏览过程当中没必要要的HTTP请求。有效期HTTP头一般被用在图片上,但它们应该用在全部组件上,包括脚本、样式和Flash组件。
浏览器(和代理)用缓存来减小HTTP请求的数目和大小,让页面可以更快加载。web服务器经过有效期HTTP响应头来告诉客户端,页面的各个组件应该被缓存多久。用一个遥远的未来时间作有效期,告诉浏览器这个响应在2010年4月15日前不会改变。
1
|
Expires: Thu, 15 Apr 2010 20:00:00 GMT
|
若是你用的是Apache服务器,用ExpiresDefault指令来设置相对于当前日期的有效期。下面的例子设置了从请求时间起10年的有效期:
ExpiresDefault "access plus 10 years"