网站优化离不开前后端的互相协作,但是对于前端工程师来说,在保证后端技术方案不变时,能不能只利用前端技术来优化网站呢?答案是肯定。雅虎的邮件团队总结了常用的35条网站优化最佳实践,其中就有很多实践,只要我们前端人员在日常开发过程中遵循这些实践,就可以在一定程度优化网站加载速度。
本文篇幅较长,主要内容就是网站优化的35条最佳实践,以及对每条最佳实践的说明。技术文章看起来都是很枯燥,特别对于这种篇幅较长的文章,这里先列出来有哪些实践,不是每条实践的说明都要看,才能明白这个实践的意思。
1.最小化HTTP请求 | 2.使用内容分发网络(CDN) |
3.增加Expires或者Cache-Control头 | 4.Gzip压缩内容 |
5.把CSS文件放到顶部 | 6.把Js文件放到底部 |
7.避免CSS表达式 | 8.保持Js和CSS外部引用 |
9.减少DNS查询路径 | 10.压缩js和css |
11.避免重定向 | 12.删除重复Scripts |
13.配置Etags | 14.使Ajax请求可缓存 |
15.尽早刷新缓存 | 16.使用GET方式的Ajax请求 |
17.延迟加载内容 | 18.预加载组 |
19.减少DOM元素数量 | 20.跨域分离内容 |
21.减少iframe | 22.不要404 |
23.减少Cookie大小 | 24.内容使用没有cookie的域 |
25.减少DOM访问次数 | 26.优化事件处理 |
27.选择<link>而不是@import | 28.避免Filter |
29.优化Images | 30.优化CSS Sprites |
31.不要在HTML中使用过大的Images | 32.favicon.ico最小化以及可缓存 |
33.内容保持在25K以下 | 34.把内容打包成一个复合文档 |
35.避免空的Image src |
下面就是这些实践的详细说明。
1.最小化HTTP请求
80%的终端响应时间花费在前端,这其中的大部分时间又浪费在下载页面内容上,页面内容包括图片,样式表,脚本,flash等等。减少页面内容的数量,转而就减少了渲染页面需要的HTTP请求数量。这是优化页面的关键。
减少页面内容的一种方法是简化页面设计。但是是否存在其它方法既可以用丰富的内容来构建页面,又可以获取快速的相应时间呢?下面的几种技术即可以减少HTTP请求数量,又可以支持丰富的页面设计。
合并文件是一种减少HTTP请求的方式,通过合并多个Js文件到一个Js文件,合并多个CSS文件到一个CSS文件的方式。文件合并是非常有挑战性的,因为每个页面的Script和CSS都不一样,但是如果在你的发布过程中有这个步骤确实可以响应时间。
CSS Sprites是减少图片请求首选的方法。把你的背景图片合并到一张图片中,使用CSS的background-image和background-position属性来展示期望的图片部分。
Image maps 合并多个图片到一个图片中。图片总大小是一样的,但是减少了一定数量的HTTP请求,加快了页面展示速度。只有当页面中的图片是连续的,Image maps才有用,例如导航栏。定义Image maps的坐标可能非常乏味并且也容易出错,为导航使用Image maps也不是很方便,所以不推荐这种方式。
Inline images 使用data:URL scheme把图片数据内嵌到当前页面中。这种方式会增加HTML文档的大小。把Inline images写到(缓存的)样式文件中是减少HTTP请求的同时避免增加页面大小的一种方法。Inline images还没有被所有的主流浏览器支持。
2.使用内容分发网络(Content Delivery Network,CDN)
用户跟Web服务器的距离对相应时间是有影响的。从用户的角度来说,把你的内容部署在多个地理位置分散的服务器上有利于页面加载的更快。
实施内容分发,首先不要尝试把Web应用重构成分布式架构。根据不同的应用,改变架构可能会有艰巨的任务,比如在不同的服务器之前同步session状态,复制数据库事务。这样的话应用架构这一步可能就会导致这种尝试延迟。
记住80%到90%的终端响应时间花费在下载页面内容上面:iamges,css,js,flash等等,这是性能的黄金法则。而不是以重构web应用架构的艰巨任务开始,最好首先分散静态内容。这样不仅最大化的减少响应时间,而且利用内容分发网络可以变得更简单。
内容分发网络(CDN)是分布在多个地点的web服务器的集合,可以有效的为用户分发内容。给用户分发内容的服务器是根据网络距离选择的,例如,选择拥有最少网络跳转或者最快相应速度的服务器。
3.增加Expires或者Cache-Control头
这个规则有两个方面:
对于静态资源:实行"永不过期"策略,通过在header中把Expires设置尽量长点
对于动态资源:使用一个适当的Cache-Control header帮助浏览器响应条件请求
页面设计的越来越丰富,这就意味着页面中有更多的js,css,images,falsh等。首次访问页面的用户可能会有几个HTTP请求,但是通过使用Expires header你可以使这些内容缓存。这样在后续页面访问时可以避免不必要的HTTP请求次数。Expires header经常和图片一起用,但是js,css,flash都可以使用。
浏览器(和代理)使用缓存减少HTTP请求数量和大小,加快页面加载速度。web服务器在HTTP响应中使用Expires header告诉客户端内容可以被缓存多长时间。 下面就是一个Expires header例子,告诉浏览器这个响应在2010-4-15之前不会过期Expires: Thu, 15 Apr 2010 20:00:00 GMT
如果是Apache服务器,使用ExpiresDefault指令相对当前日期设置过期时间。下面就是设置请求10年过期的例子:ExpiresDefault "access plus 10 years"
谨记,如果你使用了Expires header,那么当你的文件内容有改变时,内容的文件名必须改变。这样内容才会更新。可以再文件名中加版本后,这样每次发布时文件名都会改变。
使用Expire header只会影响已经访问过你站点的用户。如果用户第一次访问站点或者浏览器还没有缓存,它不会减少HTTP请求次数。因此这种性能改善的影响依赖于用户是不是经常访问带缓存的页面。通过使用Expires header你可以让浏览器增加缓存内容数量,在后续页面访问时重用这些内容而不用在通过网络请求获取。
4.Gzip压缩内容
前端工程师做出的某些决定可以显著的减少HTTP请求以及响应的网络传输时间。终端用户的带宽速度,网络服务提供商,距网络交换节点的距离这些因素都不受开发团队的控制。但是仍有其它的因素会影响响应时间。比如,通过压缩HTTP响应来减少响应时间。
从HTTP/1.1开始,web客户端就开始支持HTTP请求的 Accept-Encoding header。Accept-Encoding: gzip, deflate
如果web服务器在请求中看到这种header,它就会用客户端列出来的方法来压缩响应内容。web服务器通过响应中的Content-Encoding header通知web客户端。Content-Encoding: gzip
Gzip是目前最流行以及最有效的压缩方法。其它你有可能看到的压缩格式是deflate,但是它不是很流行以及很有效。
Gzip通常可以把响应内容大小减少70%。目前浏览器当中接近90%的网络流量支持gzip。
对于浏览器以及代理来说还有一个已知问题就是浏览器期望的内容和它获取的压缩内容可能不匹配。幸运的是,这种情况随着旧版浏览器的使用率越来越低会越来越少。Apache模块通过添加适当的不同响应头来解决这个问题。
服务端选择用Gzip压缩内容主要依赖于文件类型,但是通常也受限于要决定压缩的内容。大部分web站点用gzip压缩html文档。当然也值的压缩脚本以及样式文件,但是很多网站都没有选择这么做。事实上,可以用gzip压缩任何文本响应内容,包括XML和JSON。Image和PDF不建议gzip压缩,因为它们都是被压缩过的。试图压缩它们不仅浪费CPU也有可能会增加文件大小。
用gzip压缩尽可能多的文件类型是减少页面大小提升用户体验的一种简单方法。
5.把CSS文件放到顶部
把CSS文件移到文档的HEAD中看起来好像可以加快页面载入速度,其实这是因为把CSS文件放到HEAD中,可以让页面逐步渲染。
前端工程师可能只关注性能,希望页面可以逐步加载,但是对于用户来说,不管页面有多少内容,希望浏览器可以尽快的展示页面。对于页面内容很多以及用户网络环境不好的情况,这一点非常重要。给用户视觉反馈(例如进度提示)的重要性不言而喻,有很多类似的研究。当浏览器加载页面时逐步加载头部,导航栏,以及顶部logo等的过程,HTML页面就相当于进度提示,这样就给等待页面加载的用户一个视觉反馈。提高整体的用户体验。
如果把CSS文件放到页面底部,在很多浏览器中可能会影响逐步渲染过程,包括IE浏览器。因为这些浏览器会阻塞渲染过程以防页面元素样式改变时重绘元素。用户就会看到一个空白页。
HTML的规范中明确声明了CSS是包含在页面的HEAD中的。
****6.把Js文件放到底部****
脚本文件会导致的一个问题就是它们会阻塞并行下载。HTTP/1.1规范建议不要在一个主机上并行下载不超过两个脚本文件。如果把图片托管到多台主机上面,那就可以并行下载多个图片文件。但是当一个脚本文件正在下载时,浏览器不会下载其他的脚本文件,即便这个脚本文件在其它主机上面。
在有些情况下想要把脚本文件移动文档底部并不容易。例如如果脚本使用document.write插入页面内容,它就不能移到页面底部。这可能会引起作用域问题。在很多情况下,还有其它的途径解决类似的这种问题。
一个常用的建议就是使用延时脚本。script的DEFER属性表明脚本不包含document.write,告诉浏览器可以继续渲染。但是也不是所有的浏览器都支持。Firefox不支持DEFER属性,IE对DEFER的支持也不尽如意。
如果脚本可以延迟加载,它就可以放到页面底部,这样就会加快页面的载入速度。
7.避免CSS表达式
CSS表达式是动态设置CSS属性的一种强大并且危险的方法。IE从5开始支持CSS表达式,但是从IE8开始弃用这种方式。下面的例子就是,背景颜色可以根据时间动态设置:
background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );
如上面展示的,expression方法接受一个js表达式参数,CSS的属性是根据js表达式的计算结果设置的。其它浏览器不支持expression方法,所以它只适用于IE浏览器。
CSS表达式的问题是计算太频繁了,不仅页面渲染缩放时,需要计算,页面滚动甚至鼠标滑过页面时都需要重新计算。如果在CSS表达式里面加个计数器来追踪CSS表达式的计算频率,把鼠标滑过页面就会发现计算次数会轻易的超过10000次。
减少CSS表达式计算次数的方法是使用一次性表达式,表达式第一次计算时会给样式属性设置一个显示值来代替表达式。如果样式属性必须根据页面活动动态设置,用事件绑定代替CSS表达式是一个好的选择。
8.保持Js和CSS外部引用
有很多性能规则来处理外部组件管理。然而,在考虑这些因素之前,有一个更基本的问题需要考虑:js和css应该包含在外部文件中,还是内联在页面里面?
使用外部文件通常会加快页面速度,因为这样js,css文件可以被浏览器缓存。如果js,css内联在页面中,每次请求页面的时候都会重新加载。这样虽然减少了http请求数量,但是增加了html的大小。从另一方面来说,如果js,css在外部文件中并且被浏览器缓存,那么后续请求不会增加html文档大小,也不会增加http请求数量(外部文件从浏览器缓存中取,不经过http请求了)。
关键是html文档使用缓存的js,css文件的频率。这个因素尽管难以量化,但是也可以通过其它维度衡量。如果一个站点用户每个session周期需要访问多个页面,并且这多个页面有共同的脚本和样式文件,那么使用缓存的文件就有潜在的好处。
一般情况下都是把js,css写到外部文件中,但是也有例外,内联适合应用在首页。首页的每个session的访问次数少,内联js,css可以加快终端的响应时间。
内联可以降低http请求,使用外部文件缓存也有好处。通常的处理是首页内联js,css,当也加载完成之后再动态下载外部文件,这样后续页面就可以从浏览器缓存中获取这些文件。
9.减少DNS查询路径
域名系统(DNS)是把域名映射到IP地址上,就像手机通讯录把人名映射到手机号上一样。当你在浏览器输入www.yahoo.com的时候,DNS解析服务连接到浏览器然后返回服务器的IP地址。DNS是有开销的,一般会花费20-120ms为主机查询IP地址。浏览器不能通过主机名下载任何东西,除非DNS解析已完成。
为了性能考虑DNS查询都会缓存。这个缓存可能发生在某一台缓存服务器上面,这个缓存服务器有用户的ISP或者局域网维护,也有可能缓存到个别用户电脑上。DNS信息保存在操作系统的DNS缓存中(windows上面的DNS client service)。大部分浏览器都是自己的DNS缓存以区分于操作系统的缓存。一旦浏览器在自己的缓存中保存了DNS记录,它就不会从操作系统里面查这条记录了。
IE默认缓存DNS查询30分钟,可以在注册表DnsCacheTimeout中指定。Firefox默认缓存DNS查询1分钟,可以通过network.dnsCacheExpiration配置项设置。
当客户端的DNS缓存都是空时(浏览器和操作系统的),DNS的查询次数就和页面中主机的DNS查询次数一样。包括页面url,图片,脚本样式文件,flash对象里面的主机名。减少独立主机名,就可以减少DNS查询次数。
减少独立主机名对页面中需要并行下载的地方有潜在的影响。避免DNS查询虽然可以减少响应时间,但是减少并行下载也会增加响应时间。这两者是相互矛盾的,作者的建议是保持2到4个单独主机,这样DNS查询不至于过多,也有一定的并行下载。
和中的代码块也应该压缩。尽管你用gzip压缩了你的脚本和样式文件,再次压缩它们仍然可以减少5%或者更多的体积。随着使用的js和css越来越多,压缩代码可以消减成本。
10.压缩js和css
压缩的做法就是移除代码中不必要的字符减少文件大小进而提升加载速度。代码一旦被压缩,所有的注释,不需要的空白字符(空格,换行,tab)都会被移除。对于Js来说,这会提高响应时间性能,因为要下载的文件大小减少了。常用的压缩js代码的工具是JSMin,YUI Compressor。YUICompressor也可以压缩css。和中的代码块也应该压缩。尽管你用gzip压缩了你的脚本和样式文件,再次压缩它们仍然可以减少5%或者更多的体积。随着使用的js和css越来越多,压缩代码可以消减成本。
代码混淆是处理源代码的另一种选择,它比代码压缩更复杂,因此在混淆过程中也更容易产生问题。有调查指出代码压缩可以减少21%的大小,代码混淆可以减少25%的大小。尽快代码混淆有更高的压缩率,但是代码压缩风险更低。
除了外部的脚本样式需要压缩,内联在<script>
和<style>
中的代码块也应该压缩。尽管你用gzip压缩了你的脚本和样式文件,再次压缩它们仍然可以减少5%或者更多的体积。随着使用的js和css越来越多,压缩代码可以消减成本。
11.避免重定向
重定向是使用301,302状态码来完成的。下面就是一个使用301响应的HTTP header:
HTTP/1.1 301 Moved Permanently
Location: http://example.com/newuri
Content-Type: text/html
浏览器自动的把用户带到Location字段制定的url中。重定向所有有用额信息都在header里面。响应内容通常是空的。
12.删除重复Scripts
一个页面中有两个一样的js文件会影响性能。这可能不像你想的那样不同寻常。当团队人数及脚本数量增加时,很有可能一个页面就会引入相同的脚本文件。这种情况发生时,重复的脚本会创建不需要HTTP连接而且多次执行进而会影响性能。不需要的HTTP请求发生在IE浏览器中,Firefox不会有这种现象。在IE中,如果一个外部js被引入了两次并且没被缓存,它就会在页面加载时产生两个HTTP请求。甚至当js被缓存时,当用户刷新页面后,额外的HTTP请求也会发生。除了产生浪费的HTTP请求,脚本多次也会浪费时间。这种情况在Firefox以及IE中都会发生,不管js文件有没有被缓存。
有一种方法可以避免多次引入相同的脚本,那就是在模板系统里面实现一个脚本管理模块。这样的话,在HTML页面中就可以利用script标签引入脚本管理文件。<script type="text/javascript" src="menu_1.0.17.js"></script>
这种方式不仅可以避免相同的脚本插入多次,还可以处理脚本相关的其它情况,比如,依赖检查,把版本号加到脚本文件名后面就可以实现缓存脚本更新。
13.配置Etags
ETags(Entity tags)是Web服务器和浏览器使用的一种机制,来决定浏览器中缓存的组件是否匹配源服务器上的组件。(entity和component一样,代指:图片,脚本,样式等)ETags提供了一种可以验证entities是否修改的机制。一个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不会匹配的。这种情况在使用集群服务器处理请求时很常见。默认情况下,Apache和IIS在ETag中嵌入数据,这样大大降低了在有多个服务器的站点上进行有效性测试成功的几率。
Apache 1.3和2.x版本的ETag格式是inode-size-timestamp。尽管一个指定的文件在不同的服务器中保存在相同的目录,有着相同的大小,权限,时间戳等等,但是不同服务器的inode是不一样的。
IIS 5.0和6.0版本也有ETags问题。IIS的Etags格式是Filetimestamp:ChangeNumber
。ChangeNumber是一个追踪IIS配置更改的计数器。一个站点的所有的IIS服务器的ChangeNumber不可能一样。Apache和IIS的ETags问题导致的结果就是,对于相同的组件在各个服务器之间不会匹配。如果ETags没有匹配,用户就不会收到小的,快速的304响应;反而会收到一个正常的200响应和组件内容一起。如果web站点只部署在一台机器上,就不会有这个问题。但是如果有多台服务器,而且又使用了Apache或者IIS的默认ETag配置,用户获取页面的速度就不会那么快,服务器有更高的负载,消耗更多的带宽,代理也不会缓存有效的内容。即使组件有一个很大的过期时间,但是每当用户点击重载或刷新时,都会创建一个条件GET请求。
如果你没有利用ETags提供的灵活验证模型,最好移除ETag。Last-Modified头验证基于组件的时间戳。移除ETag可以减小响应和后续请求的HTTP头的大小。在Apache中可以通过修改配置文件来实现:FileETag none。
14.使Ajax请求可缓存
使用Ajax的一个好处就是它能给用户提供及时的响应,因为它从后端异步的获取数据。然而使用Ajax并不能保证用户可以立马得到返回的异步js和xml响应。在许多应用中,用户是否继续等待,取决于Ajax如果如何使用。例如在内嵌页面的邮件客户端中用户为了获取符合搜索条件的邮件消息,会继续等待Ajax的请求结果。重要的是要记住一点,“异步”并不代表“及时”。
为了提高性能,优化Ajax响应也很重要。提高Ajax性能最重要的方式是缓存响应内容。来看一个例子,一个Web2.0的邮箱客户端可能使用Ajax来自动下载用户地址列表。如果用户从上次使用邮箱web app之后没有修改过地址列表,那么之前地址列表响应可以从缓存中读取,如果Ajax响应用Expires或者Cache-Control头做了缓存。浏览器必须知道何时使用缓存地址列表对于一个新请求。可以通过添加一个时间戳到地址列表Ajax url后面,表明用户修改地址列表的最新时间,例如,&t=1190241612。如果地址列表自从上次下载都没有被修改,时间戳应该不变,然后就会从浏览器缓存中去地址列表,这样就消除了一个多余的HTTP请求。如果用户修改了地址列表,时间戳就确保了新的url不会匹配到缓存中的响应,浏览器会请求更新的地址列表。
尽管Ajax响应是动态创建的,也可能只适用于单个用户,也应该被缓存。这样做可以使你的Web2.0 apps更快。
15.尽早刷新缓存
当用户请求一个页面时,后端可能会消耗200或者500ms来整合HTML页面。在这段时间内,浏览器处在等待服务器返回数据的空闲状态。在PHP中,可以使用flush()函数。它可以让服务器发送部分准备好的html响应给浏览器,然后当后端正在处理html页面的其余部分时,浏览器可以开始获取内容。
刷新缓存的最佳位置是在HEAD标签之后,因为HTML页面的head通常更容易产生,而且它可以引入任何CSS和JS文件,这样当后端还在处理时,浏览器也可以开始并行的下载引用文件。
... <!-- css, js -->
</head>
<?php flush(); ?>
<body>
... <!-- content -->
16.使用GET方式的Ajax请求
雅虎邮件团队发现当使用XMLHttpRequest时,浏览器器实施POST请求有两步,第一步发送headers,然后发送数据。所以最好使用GET请求,它只发一个TCP包(除非cookie数量很多)。在IE中url的最大长度是2K,如果数据超过2K,不能使用GET请求。
如果POST请求没有发送数据,那么它的行为就像POST一样。基于HTTP协议,GET请求是用来获取信息的,所以当你仅仅是获取数据而不是向服务端发送数据时,最好使用GET请求。
17.延迟加载组件
看一眼页面,然后思考下:“那些是一定需要在初始化渲染页面时?”。其余的内容和组件都可以延迟获取。
js文件是一个理想的备选项。例如如果js代码或者库是用来实现拖拽,动画效果的,那么就可以等待加载,因为页面中的拖拽元素在初始化渲染之后的。考虑可以延迟加载组件时也包括隐藏内容(在用户某个行为之后的才出现的内容)和图片的修饰。
当性能目标存在于其他web开发最佳实践中是非常好的。在这种情况下,渐进增强告诉我们js可以提升用户体验,但是必须确保页面没有js时也可以正常工作。因此当你确定页面正常后,你可以用一些延时加载js来增强页面效果。
18.预加载组件
预加载看起来和延迟加载相反,但是它有不同的目的。通过预加载组件,你可以充分利用浏览器空闲时间获取需要的组件(图片,样式文件,脚本文件)。这样当用户访问下个页面时,你可能已经把大部分组件缓存到浏览器缓存,然后页面加载时就会更快。有下面几种类型的预加载:
无条件预加载:一旦开始加载,你就开始获取一些额外的组件。拿google.com作为例子,看下一张sprite图片时如何请求加载的。这张sprite图片google首页并不需要,但是在搜索结果页面需要。
有条件预加载:基于用户行为,你做了一个猜测用户下一步要去那个页面,然后预加载响应的东西。在search.yahoo.com站点你会发现当你开始在输入框输入时,一些额外的组件时如何被请求的。
预期预加载:发新版本时提前加载。发布新版后,可能会有这样的抱怨“新本很酷,但是速度比之前慢”。
一部分原因可能是用户访问之前版本时有了充分的缓存,但是新版本刚开始没有缓存。你可以消除这方面的影响通过预加载一些组件,在你发新版之前。旧站点可以利用浏览器的空闲时间加载新版要使用的图片和脚本。
19.减少DOM元素数量
一个复杂的页面意味着要下载更多的字节,js中更慢的DOM访问速度。例如当你遍历500个或者5000个dom元素添加时间处理时是不同的。
大量的dom元素可能意味着有些页面标记需要被改进而不一定需要删除一些内容。你是否有过嵌套表格达到布局目的?使用很多<div>仅仅是修复布局问题?也许对标记来说有更好的语义正确的方式。
dom元素的数量很好测试,在Firebug控制台输入:document.getElementsByTagName("*").length
。
但是多少dom元素算多呢?可以参考下有良好标记的类似页面。
20.跨域分离组件
分离组件可以最大化的并行下载。考虑到DNS查考消耗,确保使用不超过2到4个域。例如你可以托管HMTL和动态内容在www.example.org,将静态组件放在static1.example.org和static2.example.org上面。
21.减少iframe
iframe允许把html文档内嵌到父文档中。理解iframe的工作原理有助于更有效的使用iframe。
iframe优点:有助于缓冲第三方内容,像广告;安全沙箱;并行下载js;
iframe缺点:即使是空白标签也有消耗;阻塞页面加载;非语义;
22.不要404
HTTP请求是费时操作,所以发HTTP请求但是获取一个无用响应(例如404)是完全没必要的,也会降低用户体验而么有任何好处。
有些站点有一些有用的404内容,这有助于提高用户体验,但是仍然会浪费服务端的资源(像数据库等)。当引入的一个外部js文件是有问题的而且找不到的时候,这种情况是很糟糕的。首先这次下载会阻塞并行下载,然后浏览器可能会把404响应内容当成js代码解析,找出一些有用的东西。
23.减少Cookie大小
之所以会使用HTTP cookies是有多种理由的,例如权限,个性化。cookies的信息在浏览器和服务端通过HTTP头交换。尽量减少cookies大小有助于减少用户响应时间。
24.组件使用没有cookie的域
当浏览器请求一个静态图片时,会随请求发送cookies到服务器,但是这些cookies在服务端又没什么用。只会增加网络流量。应该确保静态组件响应没有cookie请求。可以创建一子域专门托管静态组件。
假设你的域名是www.example.org,你可以把静态组件托管在static.example.org。如果你已经在顶级域名example.org上面设置了cookie而不是在www.example.org上面设置,那么所有的经过static.example.org的请求都会携带cookie。这种情况你可以买一个新域名,把静态组件托管到新域名上,并保持这个域名没有cookie。
把静态文件托管在无cookie的域,还有一个好处就是,有些代理可能不会缓存那些请求中带cookie的静态组件。如果你想知道你是应该用example.org或者www.example.org作为主页,考虑下cookie的影响。如果忽略www,会把cookie写到*.example.org中,所以为了性能考虑最好使用带www的子域并且把cookie写到子域。
25.减少DOM访问次数
js访问DOM元素也是耗时操作,为了更好的页面响应,你最好做到下面几点:
缓存访问到的元素引用
更新"离线"节点添加到DOM树中
避免用js操作布局
26.优化事件处理
有时页面可能会响应延时,因为DOM树中不同元素上绑定了太多的事件,然后这些事件执行的太频繁了。这就是为什么事件委托是一个好的方法。如果在一个div里面有10个button,可以给div绑定一个事件而不是给每一个button绑定事件。因为有事件冒泡,所以你可以捕获到这个事件,并且可以定位出来自于那个button。(关于事件委托可以参看这篇文章中的介绍)
如果你想开始对DOM树做点什么,你并不需要等待onload事件。通常你需要确定是你要获取的标签在DOM树中已经可用。你也没必要等待所有的图片都被下载。等到所有浏览器都支持DOMContentLoad事件,你可以用DOMContentLoad事件代替onload事件。
27.选择<link>而不是@import
前面的最佳实践有一点就是把CSS放到顶部,这样可以渐进渲染。在IE中@import的行为和在页面底部使用<link>的效果一样,所以最好不要使用它。
28.避免Filter
IE有个属性AlphaImageLoader filter,这个属性主要为了实现真彩色的PNG图片在IE7以下中的半透明效果。当浏览器正在下载图片时,使用这个filter会阻塞渲染让浏览器停止响应。它也会增加内存开销,会作用于每个元素而不是每个图片。
最好的方式是完全避免使用AlphaImageLoader,使用PNG8代替,PNG8对IE友好。如果必须要使用AlphaImageLoader,使用下划线hack _filter以防对使用IE7+的用户不利。
29.优化Images
设计师为网页设计好图片之后,在传到服务器上面之前还有很多事情要做。
你可以检查下GIFs,看看他们使用的调色板大小是佛对应图片颜色数。使用imagemagick工具,非常容易检查,使用命令 -verbose image.gif。当你看到一个图片使用了4种颜色,一个256颜色“槽”在调色板中,这说明还是有提升空间的。
尝试把GIFs转成PNGs,看下大小是否减少了。通常是有所减小的。开发者经常在使用PNG时犹豫,担心浏览器支持限制,但是现在这种事不会发生了。现在唯一要担心的是真彩色PNG的透明度通道,GIF不是真彩色,不支持透明度变化。因此GIF可以做的,PNG也可以做(动画除外)。在imagemagick工具中使用下面命令可以安全的使用PNG:convert image.gif image.png
在pngcrush工具(PNG优化工具)中处理所有的PNG。在jpegtran工具中处理所有的JPEG。这个工具无损JPEG操作,可以用来优化移除图片中的注解以及无用信息(例如EXIF信息)。
30.优化CSS Sprites
在sprite中水平排列图片比垂直排列图片更剩空间。
在sprite中组合同类色可以降低颜色数量。理想情况下小于256色,这样更适应PNG8。保持移动友好性,sprite中的图片之间不要留大的空白。这样虽然对文件大小没多少影响,但是用户代理可以更少的内存把图片解压成像素地图。100X100的图片有一万个像素,1000X1000就有一百万个像素。
31.不要在HTML中使用过大的Images
不要使用超过你需要的大图片,因为你可以在HTML中设置宽高。如果你需要<img width="100" height="100" src="mycat.jpg" alt="My Cat" />
,那么你的图片(mycat.jpg)应该是100X100,而不是500X500。
32.favicon.ico最小化以及可缓存
favicon.ico存在服务端根目录。即使你不关注它,但是浏览器仍然需要请求它,因此最好不要用404响应。因为在同一台服务器上面,浏览器每次请求它时也会带着cookie。这个图片也会干扰下载序列,例如在IE浏览器中当你请求额外的组件,favicon会在这些组件之前下载。所以为了减少favicon的缺点,需要做到下面几点:
使用小的favicon,最好1K一下。设置一个你认为合适的Expires header。你可以设置Expires header为几个月。为了做明智的决定,你可以检查favicon.icon的上次修改时间。
33.内容保持在25K以下
这个限制是基于这样一个事实,iphone不会缓存超过25K的内容。注意这是未压缩大小。这也说明了压缩的重要性,但是仅仅gzip压缩是不够的。
34.把内容打包成一个复合文档
把内容打包成一个复合文档相当于带附件的邮件,可以让你通过一个HTTP请求获取多个内容。使用这项技术之前,先确认下用户代理是否支持这种技术。
35.避免空的Image src
空src属性的Image经常会出现。主要有两种形式:
HTML形式:<img src="">
js形式:var img = new Image(); img.src = "";
这两种形式都有同样的影响:浏览器给服务端发送另外的请求。
IE对当前页所在目录发请求。Safari和Chrome对当前页面本身发请求。Firefox3和之前的版本行为和Safari和Chrome一样,但是3.5以上版本不会发请求。Opera碰到这种情况也不会发请求。
全文完