w3ctech

网站性能优化35计(YUI)

加快网站访问速度的最好做法

(在网站优化方面)有着不俗表现的团队已经总结出一些加快网站访问速度的最佳做法。35个最佳做法被分成了7个大类。分别为内容、服务器、cookie、层叠样式、javascript、图片、移动端。

减少HTTP请求

终端用户80%的响应时间是花在前端上,(80%响应时间)大部分被用来在一个网页中下载所有组件(包括图片、样式、脚本、Flash动画等)。减少组件的数目反过来就会减少渲染页面所需HTTP请求的数目。这是加快网页访问速度的关键。
减少页面中组件数目的方法会简化页面的设计。是否存在一个在页面上创建丰富多样的组件且也能实现快速响应的方法?这里有一些减少HTTP请求数目的技巧(即使是仍然要求页面支持丰富的页面设计)。
合并文件是通过合并所有脚本文件成单一脚本以及合并所有样式成单一样式表的方式来减少HTTP请求数目。当这些脚本集合和样式集合里面的脚本元素和样式元素分别处于不同的页面,合并文件就会变得富有挑战性,但是如果能够使得合并文件成为释放进程的一部分的话,就能够改善响应时间(长的问题)。
CSS雪碧图是比较偏爱的减少图片请求数目的方法。将背景图片合并到一张图片上,然后使用CSS背景图片和背景属性来呈现期望的图片片段。
图片地图将许多图片合并到单一图片上。图片所有的尺寸都是一样的,不过(这样的话),可以减少HTTP请求数目加速页面访问速度。只有图片像导航栏一样连接在一起,图片地图才能够起作用。设定图片地图坐标是件令人乏味且极易出错的差事。用图片地图来浏览也是不易实现的,因此图片地图这种方法不值得推荐。
在实际页面中行内图片使用数据:URL协议来嵌入图片数据,这个会增加HTML文件的大小。将行内图片合并到已缓存的样式表中,可以减少HTTP请求,也可以避免页面的大小变大。在跨主流的浏览器时行内图片没有得到全部支持。
在页面中减少HTTP请求数目是最开始需要做的事情。对于首次访问网站的游客来说,提升性能是最重要的向导。正如Tenni Theurer的博客所写的浏览器缓存用法-大揭露,40%到60%日常游客访问你的网站是不带缓存的,对于首次访问你网站的游客来说,打造更好的用户体验的关键是让你的网站访问速度快。

使用内容分布网络

将web服务器面向用户,(这样的话)会对响应时间产生(非常的)影响。跨多个不同地域的分布式服务器来部署你的项目,从用户的角度来看,页面载入速度会变得更快。但是我们应该从哪方面开始呢?
实现跨地域(部署)项目的第一步,不要企图(希望)通过重新设计你的web应用来让应用能够在分布式架构上跑起来,而是通过改变架构,使得它能够记录同步的会话状态,并且能够在跨服务器域时进行数据库事务复制的操作。试着缩短用户与你(被延期或者不能通过的)项目的距离,(这就是实现)应用架构的步骤。
记着终端用户响应时间的80%-90%是被用在(完成)下载页面中的图片、样式表、脚本、flash动画等元素操作上。这就是性能黄金定律。与其开始重新设计复杂度非常高的应用架构,不如分散项目中的静态资源。不仅可以实现大幅度的减少响应时间,由于内容分布网络的存在,使得这些变得异常简单。
内容分布网络是通过一系列分布在不同地域的web服务器将服务器上的资源稳定高效的分发到用户手中,对于一个特定的用户来说,选择分发资源的服务器是基于网络距离的检测机制。例如当到达服务器所需的跳跃数更少时或者当服务器拥有最快的响应时间时,那么这个服务器就会被选择上。
一些大型的互联网公司都有自己的CDN服务,但是使用一些CDN服务提供商像Akamai TechnologiesEdgeCastlevel3提供的CDN服务是划算的。对于正处于创业的公司和私人网站来说,CDN服务所需的花费是不允许的,但是当你的目标用户群变得越来越大且越来越广,实现缩短响应时间,提高访问速度的功能是很有必要的。在雅虎,实现将静态项目从他们自己的应用web服务器迁移到CDN(有上面提到的第三方CDN也有雅虎自己的CDN)后,缩短响应时间的程度高达20%甚至是更多。(项目)迁移到CDN只是一些相关代码的改变,然后就可以自动提升网站的响应速度。

增加过期时间或者可控制缓存的请求头

这个优化做法(需要注意)两个方面:

对于静态组件:通过设置未来响应报头中的过期请求头来实现“永不过期” 对于动态组件:使用合适的可控制缓存的请求头来帮助浏览器实现条件化的请求

网页设计变得越来越丰富生动啦,这就意味着在页面中需要使用更多的脚本、样式表、图片、和flash动画。首次访问你网站的游客可能会发出多条HTTP请求,但是通过使用过期请求头可以使这些组件(图片、样式表等)缓存起来。这样可以避免不必要的HTTP请求在页面视图队列中的情况。过期请求头经常应用在图片方面,但是过期请求头应该被使用在脚本、样式、和Flash动画等所有的组件中。
浏览器(代理)使用缓存机制来减少HTTP请求的数量以及降低HTTP请求文件的大小,这样使得网页载入更快。web服务器在HTTP响应报头中使用过期报头来告诉客户端组件应该缓存多久。下面是未来的响应报头中的过期报头,告诉浏览器这个响应在2015年4月15日之前不会失效。 Expires: Thu, 15 Apr 2010 20:00:00 GMT

如果你的服务器是Apache,使用默认过期属性直接设置相对当前时间一个过期时间。这是一个直接设置过期属性值为发出请求后的10天的例子。
ExpiresDefault "access plus 10 years"

记住,如果你使用未来响应中过期报头时,无论什么时候改变组件,你都要改变组件的文件名。在雅虎,我们经常会在构建过程中添加这一步:将版本号嵌入到组件的文件名中,例如,yahoo_2.0.6.js。
仅仅只有在用户已经访问过你的网站情况下,使用未来响应报头中过期报头才会影响页面视图。当用户首次访问你的网站并且浏览器没有缓存时,HTTP请求的数目对页面视图没有影响的。因此性能改善对视图的影响主要取决于准备好缓存(“准备好的缓存”就是指缓存中已经包含页面中所有的组件)的用户多久访问你的页面。我们可以在雅虎上测试“准备好的缓存”并且发现存在准备好的缓存的页面数目达到75%-85%。通过使用未来响应报头的过期报头,你可以发现浏览器缓存的组件数目增多,在网络连接过程中不发送单一字节的请求情况下,在页面中重新使用缓存。

Gzip组件

通过网络传输HTTP请求和响应所花费的时间可以通过前端工程师的策略而被大大的减少。终端用户的带宽速度、网络供应商、靠近对等的可交换节点超出开发团队的控制的事情是真的。但是还有其它因素影响响应时间。通过压缩来减少HTTP请求的尺寸大小的方式,进而缩短响应时间。
从HTTP/1.1开始,web客户端表明在HTTP请求报头中支持有接受编码报头的压缩形式。
Accept-Encoding: gzip, deflate 如果web服务器发现请求报头中含有接受编码报头,可能会使用客户端列出的(一个)方法来压缩响应报头。web服务器会通过响应报头中的内容编码报头来告知客户端。 Content-Encoding:gzip Gzip是现在这个时期最流行的且效率高的压缩形式。由GNU项目进行发展维护,由RFC 1952标准化。你看到过的其它压缩形式deflate,但是它的压缩既不流行效率也不高。
Gzip通常可以减小70%响应报头尺寸。现在大约90%通过浏览器实现网络传输的方式都声称支持gzip,如果你在使用Apache,它的版本决定配置gzip模块:Apache1.3使用mod_gzip然而Apache2.x使用mod_deflate
浏览器和代理可能导致浏览器期望得到内容和由于使用压缩后所接收到的内容不匹配,这个问题在业内众所周知。幸运的是,随着使用老浏览器(的用户)数量慢慢减少,这些边界问题也慢慢减少。Apache模块通过自动添加合适的变化响应报头来解决这个问题。
服务器基于文件类型来选择(什么样的东西)使用gzip压缩,但是在服务器决定对文件使用压缩时一般会有很多限制的。大多数网站使用gzip压缩HTML文档。使用gzip压缩脚本和样式非常值得一试。但是许多网站错失这个机会。事实上,压缩响应报头中任何文本(包括XML和JSON等)值得一试。图片 和PDF文件不应该使用gzip方式压缩,因为它们已经经过压缩啦。尝试使用gzip方式压缩不仅浪费CPU也会增加文件的大小。
尽可能对很多类型的文件使用Gzip,是减少网页尺寸以及提升用户体验的简单可行的方法。

将样式放在网页顶部

我们在雅虎做网站性能研究的期间,发现将样式移至文档HEAD标签里面可以使得网页实现更快载入效果。这是因为将样式放在HEAD标签里面可以网页逐步渲染。
前端工程师关心性能,希望页面可以逐步加载;也就是说,我们想浏览器能够尽可能快地显示(从服务器请求过来的)任何内容。这个(逐步加载)对于网络不是很好的用户以及页面中含有大量内容的情况显得尤为重要。给用户可视化的反馈(进度条)的重要性已经被好的研究及证明。在我们的案例中,在浏览器逐步载入页面的头部、导航栏、顶部logo等过程中,HTML页面就是进度显示器。对于在等待页面加载的用户来说,所有服务都可以作为可视化反馈。这些改善整体上的用户体验。
将样式放在文档底部带来的问题是这种做法会禁止许多浏览器(包括IE)的逐步加载功能。浏览器块渲染就是用来避免如果页面的样式改变必须进行页面元素重绘的情况。用户卡住后界面就会一直处于空白页。

将脚本放在网页底部

脚本带来的问题就是并行批量下载。HTTP/1.1规范表明,浏览器针对每个域名并行下载的组件不超过2个。如果你的图片来自多个域名,你就可以并行下载多于2个的组件。当一个脚本正处于下载时,浏览器不会开始任何其它下载(即使是来自不同域名的脚本)。
在一些情况下不宜将脚本放在网页底部。例如脚本使用document.write方法嵌入部分页面内容,不能移到页面底部,可能是存在作用域问题。在很多案例中,存在很多针对这些情况的解决方案。
经常提到的另外一个解决方案是使用defer属性的脚本。DEFER属性表明不包含document.write方法,这是浏览器可以持续进行渲染的线索。不幸的是,FireFox不支持DEFER属性。在IE浏览器,这个脚本可能延迟,但是效果甚至是不如我们期待的那样。如果脚本可以延迟,那也就可以移到网页底部。这样的话,可以使网页更快的载入。

避免CSS表达式

CSS表达式是一个可以动态设置CSS属性值强大的方法。支持IE5以上,但是CSS表达式在IE8废弃掉。来个例子,用CSS表达式来每个小时轮流设置背景色:
background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );

正如上面展示,表达式中可以接受JavaScript表达式。CSS属性是通过计算JavaScript表达式得到的结果来设置的。这个表达式方法被其它浏览器所忽略。所以在IE浏览器使用CSS表达式设置CSS属性是有用的,但是跨浏览器需要创建一致的属性。
表达式带来的问题是动不动就要计算(计算的次数比大多数人期望还要多)。不仅在页面重新渲染以及页面大小改变时需要计算,而且在页面出现滚动条甚至在页面上移动鼠标也需要计算。给CSS表达式添加一个计数器就能够让我们保存CSS表达式什么时候以及多久计算一次的记录。在页面上移动鼠标能轻易产生超过1000次计算。
减少CSS表达式计算次数的方法是使用只计算一次的表达式,第一次计算表达式的结果被显式设置样式属性(代替CSS表达式)。如果动态设置样式属性自始至终都存在页面中,另外一个方法就是使用事件处理器代替CSS表达式。如果你必须使用CSS表达式,请记住它们可能被计算成百上千次以及可能会影响页面的性能。

创建JavaScript和CSS外部文件

许多性能规则处理的是外部组件如何管理。然而,在顾虑出现之前,你应该问又一个基础问题:JavaScript和CSS应该包含在外部文件中,或者内联在页面本身?
在现实世界中使用外部文件通常可以促成快响应页面产生,因为外部JavaScript和CSS文件会被浏览器缓存下来。每次HTML文档被请求时,JavaScript和CSS都会内联在下载好的HTML文档。减少HTTP请求数目是非常必要的,但是增加了HTML文档的尺寸。另一方面,如果JavaScript和CSS存在(浏览器缓存的)外部文件中,HTML尺寸会减少(没有增加HTTP请求数目)
关键因素是外部JavaScript和CSS组件缓存的频率和HTML文档请求的数目。这个因素很难被量化,可以通过不同的指标来测量。如果浏览你网站的用户有多个页面视图和一个会话以及许多页面重用一样的脚本和样式,有这么大的潜能得益于缓存的外部文件。
许多网站性能测试跌落至测试标准的中等水平。对这些站点来说,最好的解决方案通常是将JavaScript和CSS文件部署成外部文件。唯一的例外含有主页的在线网页像Yahoo!'s front pageYahoo!。每次会话含有更少页面视图的主页可能找到在线的JavaScript和CSS导致终端用户更快的响应时间。
前台页面一般是许多页面视图的第一个。利用在线提供简化的HTTP请求,也可以通过使用外部文件实现可缓存的特性等小技巧。另外的技巧就是在前台页面中直接插入JavaScript和CSS,但是在页面载入完成后才会动态下载外部文件。引用外部文件的页面应该早已缓存在浏览器中。

减少DNS查询

域名系统(DNS)实现了域名和IP地址的映射,就像电话本实现了人名和电话号码的映射。当你将www.yahoo.com写进浏览器地址栏时,通过浏览器进行DNS解析然后返回服务器的IP地址。DNS是有代价的。给定的域名用DNS查询IP地址通常需要花费20-120毫秒。在DNS查询未完成之前浏览器不进行从这个网址任何下载。
为了更好的性能DNS查询是会缓存的。这个缓存机制会出现在专门的可缓存的服务器中(包括用户的ISP服务商或者本地的局域网),但是DNS查询在用户个人电脑上也会有缓存。DNS信息保存在操作系统的DNS缓存池(DNS客户端服务存在Microsoft Windows)。大多数浏览器有自己的缓存,是从操作系统中缓存分离出来的。只要浏览器在自己的缓存中保存DNS查询记录,就不会和操作系统里这条DNS记录的请求相冲突。
IE缓存DNS查询默认需要30分钟,专门通过DnsCacheTimeout进行注册表设置。FireFox缓存DNS查询需要1分钟,可以通过network.dnsCacheExpiration配置设定。(Fasterfox改变配置需要1小时)。
当客户端没有DNS缓存(浏览器和操作系统),网页里含有多少个独一无二的域名就要进行多少次DNS查询。独一无二的域名包括在页面中使用域名的URL、图片、脚本文件、样式、Flash等。
减少唯一域名的数量就可以减少页面中的并行下载。避免DNS查询缩短响应时间,但是减少并行下载可能增加响应时间。我的意见是在至少2个但不多于4个域名情况下,将这些组件分离。这样实现在减少DNS查询和允许高程度的并行下载两种情况的折中。

压缩JavaScript和CSS

压缩是从代码中去除不必要的字符进而实现减少尺寸然后缩短载入时间的惯用技巧。当所有注释被移除而且所有不需要的空白字符(空格、换行和Tab)被清除时,代码也就是mini化的啦。这种移除对于JavaScript来说,会改善响应时间性能,因为下载文件的尺寸被减小啦。压缩JavaScript代码时两个流行工具是JSMinYUI Compressor。YUI压缩也可以压缩CSS代码。 Obfuscation是另外一个应用在源代码的优化方法。比压缩更复杂,因此可能会产生错误(由于Obfuscation调用自身)。在美国排名前十网站进行一次调查发现,压缩实现了21%尺寸的减少VSObfuscation实现了25%尺寸的减少。尽管Obfuscation拥有更好的减少文件尺寸特性,但是压缩JavaScript的压缩方式风险更小。
另外外部的脚本和样式、内联的以及代码块都应该被压缩。即使你使用gzip你的脚本和样式,使用Minification压缩这些脚本和样式仍然减少尺寸大小高达5%。随着JavaScript和CSS使用的人越来越多以及尺寸越来越大,因此通过压缩你的代码来获取savings。

应避免重定向

重定向可以通过状态码301和302来实现,下面是301响应中HTTP报头的例子:
HTTP/1.1 301 Moved Permanently Location: http://example.com/newuri Content-Type: text/html

浏览器自动实现用户在地址栏输入特定URL的跳转。重定向所需的信息都在HTTP报头中。响应报头的body体通常是空的。不管名字,也不管是不是301或者302响应在实践中都会缓存起来的(除非另外的报头例如Expires或者Cache-Control表明会注意名字以及响应的类型)。meta属性会刷新标签,JavaScript是让用户跳转到不同的URL的另外方法。但是如果你一定要实现重定向,更偏爱的技巧就是使用标准的3xx HTTP状态码,这样做的目的是确定回退按钮是否正常工作。
记住关键事情:重定向会降低用户体验。在用户和HTML文档中嵌入重定向会延迟页面中所有元素(在HTML文档未到达之前,页面中是没有东西来渲染,也没有东西可以开始下载的)。
最浪费性能的重定向经常发生然而web开发人员通常没有意识到。当URL中本应该有正斜杠但实际上没有正斜杠(/)的时候,就会发生上面的情况。例如,去http://astrology.yahoo.com/astrology导致301响应中包含去http://astrology.yahoo.com/astrology/(注意增加了正斜杠)重定向。这种情况在Apache中通过使用Alias或者mod_rewrite或者DirectorySlash指引的(如果你使用的是Apache)。
从旧网站链接到新网址常见的方式是使用重定向。其它的包括链接到网站不同部分以及用户基于一定条件的跳转(浏览器的类型,使用者的类型等)。使用重定向来链接两个网站是很简单的然后要求少量额外的代码。尽管在一些情境下使用重定向对开发人员来说降低复杂度,但是会降低用户体验。如果两个代码路径绑定的是同一个服务器,那么另外使用重定向的方法是使用Alias和mod_rewrite。如果域名改变是使用重定向的原因的话,另外可以选择的方法是创建CNAME(创建一个从域名到其它的aliasDNS记录),然后与Alias或者mod_rewrite结合使用。

移除重复的脚本

一个页面中包含两个相同的JavaScript文件是很伤性能的。这个可能和你平常想的不一样。对美国排名前十的网站回顾可以发现:其中有两个网站包含废弃的脚本。两大因素 增加了在一个页面中出现脚本被重复的几率:团队的规模以及脚本的数量。当这件事确实发生了,重复的脚本通过创建可有可无的HTTP请求以及浪费JavaScript解析器资源的方式来损伤性能。
不必要的HTTP请求最开始出现在IE中,但是没有出现在FireFox。如果外部的脚本被引入两次而且没有被缓存起来。在页面加载期间,会产生两个HTTP请求。即使脚本被缓存起来,在用户进行页面重新载入时,额外的HTTP请求还是会出现。
另外产生浪费资源的HTTP请求,脚本进行多次计算时很耗时间的哦。不管脚本是否被缓存,多余的JavaScript执行环境还是会在IE和FireFox发生。
避免意外引入两个相同的脚本的方法是在你的模板系统中实现脚本管理模块。一般在页面中包含脚本的做法是在HTML页面中使用SCRIPT标签。

在PHP中相关的操作是定义一个insertScript函数。

另外为了防止相同的脚本被多次嵌入,这个函数可以处理脚本其它问题例如依赖性检查以及给脚本文件名添加版本数字来支持未来的过期报头。

配置实体标签

实体标签(ETags)是web服务器和浏览器都使用来确定在浏览器缓存中的组件是否和原来服务器的组件一致的机制。(“实体”其实是组件的别名:图片、脚本、样式等)ETags添加是为了给验证实体提供一种比last-modified更灵活的更新实体机制。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。
ETags带来的问题是ETags通过属性构建使得对特定服务器(托管一个网站)来说,ETags是唯一的。当浏览器从一个服务器得到原始组件然后从另外一个服务器更新组件时(在网站中使用服务器集群来处理请求很常见的情况),ETags是不会匹配的。默认情况下,Apache和IIS在ETag中嵌入数据来自动降低有多个服务器的网站进行更新测试成功的概率。
Apache1.3和2.x中ETag形式是inode-size-timestamp。尽管在不同的服务器存在相同目录的指定文件,相同的文件大小、权限、时间戳等,但是文件的inode从一个服务器到另一个服务器是不同的。
对于ETags,IIS5.0和IIS6.0有相同的问题。在IIS上ETags的形式是Flietimestamp:ChangeNumber。ChangeNumber是个计数器被用来记录IIS服务器配置改变的痕迹。跨网站后面所有的服务器时ChangeNumber是相同的,这是不可能的。
最后的结果是Apache和IIS服务器针对相同的组件产生的ETags从一个服务器到另一个服务器是不匹配的。如果ETags不匹配,用户就不能接收到为ETags设计的小巧快速304响应。而是收到含有组件所有数据的普通200响应。如果你的网址只托管在一个服务器上,这就不是问题。但是如果你的网站托管在多个服务器上,并且你使用的是默认的ETags的Apache或者IIS,用户的页面会一直在载入,服务器会一直在响应,服务器需要更高的带宽,代理不能有效的缓存内容,即使你的组件有未来过期的报头、条件化的GET请求仍然使得无论用户什么时候都会碰上页面处于重新载入。
如果你不利用ETags提供的灵活的更新模型,去除ETags是更好的选择。last-Modified报头更新基于组件的时间戳。移除ETags减少了请求和响应HTTP报头尺寸。This Microsoft Support article描述如何移除ETags。在Apache中在Apache配置文件中简单的添加下面一句话: FileETag none

Ajax缓存化

Ajax优势是能够给用户提供即时反馈,因为Ajax异步从后台请求数据。然而,使用Ajax不能保证用户不会玩弄拇指来等待从后台返回异步的JavaScript和XML响应。在很多应用中,无论用户是否等待取决于Ajax如何用。例如,在基于web的邮件客户端中,用户等待Ajax请求(找到所有和搜索关键字匹配的email信息)返回的结果。记住异步并不暗示即时的观念是很重要的。 为了提升性能,优化Ajax响应很重要。提升Ajax性能的最重要的方式是将响应缓存起来,正如在Add an Expires or a Cache-Control Header讨论的那样。一些规则也适用于Ajax:

Gzip组件 减少DNS查询 压缩JavaScript 避免重定向 配置ETags

让我们看个例子。Web2.0邮件客户端可能使用Ajax来下载用户的地址簿实现自动填充。如果用户没有修改他的地址簿(自从上次使用邮件web应用),先前的地址簿响应可以从缓存中读取(Ajax响应用未来响应报头中过期报头或者缓存可控的报头)。当使用先前缓存的地址簿响应或者进行新的请求,应该通知浏览器。这个可以通过给地址簿Ajax URL增加时间戳表明上次用户修改了地址簿的方式解决,例如,&t=1190241612。如果自从上次下载后地址簿没有被改变,时间戳将会是一样的并且地址簿会从浏览器缓存(排除额外的HTTP请求)。如果用户改变他的地址簿,时间戳会确保新的URL不会匹配已缓存的响应。然后浏览器会请求更新后的地址簿入口。
即使你的Ajax响应是动态生成的,可能对单个用户适用,响应仍然可以被缓存。做这些可以使你的web2.0应用更快。

提前刷新缓存区

当用户请求页面,后台服务器将数据组装成HTML页面需要花200到500ms。在这段时间期间,浏览器处于闲置状态,正如它等待数据到达。在PHP中你有flush函数。允许你发送部分准备好的HTML响应,以便于在后台正忙于处理页面剩余部分的时候,浏览器可以开始读取组件。在庞大的后台或者轻量前台中可以看到这些优势。
事例: ...

... 

雅虎!搜索倡导调查研究,然后真实用户测试证明使用这些技巧的益处。

AJAX请求使用GET方法

雅虎邮箱团队发现,当我们在使用XMLHttpRequest时,浏览器实现POST方法包含首先发送请求头,然后发送数据的两步过程。所以使用GET方法是最好的,仅发送一个TCP包(除非含有许多cookies)。IE中URL最大长度为2k,所以如果需要发送超过2k数据,就不适合使用GET方法。
有趣的副作用是不发送任何数据的POST方法行为像GET方法。基于HTTP参数,GET方法被用来检索数据,因此当你仅仅请求数据,正如反对发送数据存储在服务器端时,使用GET方法才有意义(语义上)。

后载入组件

你可以近距离查看自己的网页然后问自己:“为了渲染页面必须需要的东西是什么?”。内容剩下部分和组件可以等待。
对于拆分操作,JavaScript是理想的候选者(在加载事件开始前和结束后)。例如你有JavaScript类库的代码,可以实现拖动、下落、动画功能,这些操作会出现等待,因为在页面上拖动元素紧跟着初始化渲染。其它需要可以实现后载入功能的地方包括隐藏的内容(用户行为后内容就会出现)和折叠处图片。
工具可以让你以自己最大的能力解决问题:YUI图片加载器允许你延迟载入折叠的图片以及YUI实用工具是在电脑不工作时包含JS和CSS的简单方法。例如在自然状态下观察雅虎主页(Firebug的网络面板打开)。
当性能目标符合其它web发展过程最好的事例规律,这样才是好的。在这种情况下,渐进式优化处理方式的想法告诉我们,支持的时候JavaScript能够提升用户体验,甚至是在没有JavaScript的时候必须确保页面能够奏效。因此在你确定页面能够很好奏效之后,你才能用一些后载入的脚本提升页面(给你很多铃和口哨,例如还可以进行拖动、下落以及动画)。

预先载入组件

预先载入看起来好像后载入的对立面,但是事实上预先载入拥有不同的目标。通过预先载入可以让你利用浏览器空闲的时间以及请求你将来需要的组件(像图片、样式、脚本)。使用这种方式,当用户浏览下一个网页时,大多数组件已经被缓存在浏览器中,对用户来说,页面会更快的载入。
事实上预先载入具有以下的类型:

无条件的预先载入-载入一结束,你就可以开始读取额外的组件。将google.com作为检查雪碧图是否是请求载入的例子。google.com主页不需要雪碧图,但是按顺序呈现搜索结果的页面是需要雪碧图的 有条件的预先载入-基于用户的动作,你可以根据一些经验来做出猜想用户下一步将朝向哪里然后相应的进行预先载入。在search.yahoo.com你就可以发现,在你开始准备在输入框中输入,额外的一些组件是如何被请求的 期望的预先载入-开始重构之前预先载入。重构之后你经常听见新网站很爽,但是比以前的慢,这样的事情经常发生。部分问题是由于用户带有全部的缓存来访问旧网站,但是新网站经常是处于没有缓存的状况。你可以通过在你进行重构之前预先载入一些组件的方式来减轻副作用带来的影响。旧网站可以利用浏览器处于空闲的时机来请求新网站使用的图片和脚本

减少DOM元素的数量

复杂的页面意味着很多字节数据需要下载,也意味着JavaScript操作DOM速度很慢。在增加一个事件监听器情况下,如果在一个页面中循环500或者5000 DOM元素时,事件监听器会受到影响。
大量的DOM元素可以作为应该改善页面的标签没有必要出去页面的内容的征兆。为了布局你使用嵌套的表格?仅仅是为了修正布局问题你放弃许多?也许有一个更好并且更语义化的方法来处理标记。
对布局帮助更大的是YUI CSS实用工具:grids.css能帮助你处理大部分布局,font.css和reset.css可以帮助你去除浏览器默认样式。这是一个开始考虑标记的机会。例如仅仅在语义上有意义才使用,而不是因为div可以渲染出一条直线。
DOM元素数目很容易测出来,只需要在Firebug的控制台写上: document.getElementsByTagName('*').length 多少DOM元素算太多?检查其它相似有良好的标记的页面。例如雅虎主页属于非常繁忙的页面,然而元素数目低于700(HTML标签)。

跨域分离组件

分离组件允许你并行下载达到最大化。确定你使用不超过2-4个域,因为DNS查询惩罚。例如,你可以将HTML以及动态内容绑定www.example.org然后分离静态组件绑定static1.example.org以及static2.example.org。为了解更多内容查阅由Tenni Theurer和 Patty Chi编写的Maximizing Parallel Downloads in the Carpool Lane

减少iframes的数量

iframes允许HTML文档插入到父文档中。明白iframes如何工作的非常重要,因此可以高效的使用iframes。
pros:

帮助减慢第三方像广告、徽章 安全沙箱 并行下载

cons:

即使空白,代价依然高 阻塞页面载入 无语义

没有404

HTTP请求代价是昂贵的,因此创建HTTP请求以及得到无用的响应(例如404没有找到)是完全没有必要的,并且会降低用户体验,没有任何好处。
一些网站拥有具有帮助意义的404“你意味着X?”,对用户体验来说很好,但是也浪费服务器资源(数据库等)。效果尤其不好的是给出外部JavaScript文件连接是错误的,然后结果也是404。首先阻塞并行下载。下一步浏览器可能尝试解析404响应体,好像是JavaScript代码,努力寻找可以适用于它的东西。

HTTP的cookies由于许多原因而被使用(例如权限认证以及私人原因)。cookies中包含的信息会在web服务器和浏览器之间的HTTP报头进行交换。使得cookies的尺寸尽可能的小,将对用户响应时间造成的影响降到最低是非常重要的。
核对更多的信息,请查阅Tenni Theurer和Patty Chi编写的“When the Cookie Crumbles”。调查后得到实际结果如下:

去除不必要的cookies cookies的尺寸尽可能的小,将对用户响应时间造成的影响降到最低。 在合适的域级别设置cookies应该注意,所以其它子域名不会受影响。 合理设置过期时间。太早的过期时间或者不设过期时间,会更快的移除cookies,缩短用户的响应时间。

当浏览器对静态图片发起请求,然后将cookies随着请求一起发过去时,对服务器来说这些cookies没有任何作用。因此这些cookies只会创造网络流量(找不出更好的理由啦)。你应该确定静态组件是以无cookies方式发送请求的。创建子域名来托管你的静态资源。
如果你的域名是www.example.org,你可以将静态资源托管在 static.example.org。然而,如果你已经在顶级域名example.org设置了cookies而不是在www.example.org,然后向static.example.org发送的请求都包含这些cookies。在这种情况下,你可以买一个全新的域名,托管你的静态组件,然后保持域名不存在cookie。YouTube使用ytimg.com,Amazon使用images-amazon.com等。
将静态组件托管在无cookie的域名的好处就是一些代理在处理含有cookie请求时拒绝缓存这些组件。与无cookie域相关的小贴士中说到,如果想在你的主页中使用example.org或者www.example.org,请考虑cookie的影响。省略www让你对于*.example.org不得不写入cookie,所以出于性能考虑使用www子域名以及子域名写入cookie是最好的。

DOM读取最小化

用JavaScript读取DOM元素是很慢的,为了实现响应式页面,你应该:

访问后的元素应缓存 更新offline节点然后将节点添加到DOM树 避免用JavaScript处理布局

更多信息应关注Julien Lecomte在YUI大讲堂关于Ajax应用高性能的演讲

开发灵活的事件处理器

有时候感觉网页没有响应性特性可言,这是因为太多的事件处理器绑定了DOM树的不同元素,而且处理器经常执行。使用事件委托机制是个不错的方发。如果在div里有10个按钮,给div包装器绑定一个事件处理器,而不是给每一个按钮绑定一个处理器。事件冒泡,所以你可以捕捉这个事件然后可以得出事件来源于哪个按钮。
为了使用DOM树做些事情你也没必要等待载入事件。所有你需要东西都是你可以在DOM树中访问到的元素。你也没必要等待所有图片下载完。DOMContentLoad是你要使用的事件,而不是onload,但是只有当所有浏览器支持这DOMContentLoad事件,你才可以使用YUI事件实用工具(含有onAvailable方法)。
更多信息应关注Julien Lecomte在YUI大讲堂关于Ajax应用高性能的演讲

选择使用不要使用@import

早前好的实践表明,CSS应该放在页面最上面以便于允许依次渲染。在IE@import和在页面底部使用表现一样,因此好的做法是不使用。

避免过滤器

IE属性AlphaImageLoader过滤器是用来修复在IE7以下PNG图片底色半透明问题,过滤器带来的问题是当图片正处于下载时,阻塞渲染以及冻结浏览器。过滤器也会增加内存消耗,应用于每个元素,而不是每张图片。因此这个问题就是成倍增加。
最好的方法就是完全避免使用AlphaImageLoader然后用优雅的层次低的PNG8代替(PNG8在IE表现还好),使用带下划线的hack过滤器(为了不会对IE7以上用户造成影响)。

优化图片

设计师为你的网页设计了图片后,在你将图片通过FTP传送到你的web服务器之前,你仍然还可以尝试一些事。

你可以检查GIF图片,然后看是否调色板颜色的数目对应于图像中颜色的数目。使用imagemagick ,很容易检查使用冗长的image.gif,当你看到一图片使用4种颜色以及调色板中256颜色的插槽,还有改善的空间。 试着将GIF图片转换成PNG图片,然后看看这是不是个比较节约的方法。通常,由于浏览器对png图片的支持有限制,所以开发人员经常在是否使用png图片上犹豫,现在那件事已经过去啦,真正的问题是PNG图片底色透明度,问题又来了,GIF图片没有底色也不支持透明度动态变化。GIF图片可以做的事情,PNG8也能做(除了动画)。简单的imagemagick命令导致PNG图片安全使用问题:转换gif图片 png图片,我们经常说的一句话:给PNG一次机会。 让你的PNG图片运行pngcrush(或者任何其它优化工具)。例如:pngcrush image.png -rem alla -reduce -brute result.png。 让你的JPEG图片运行在jpegtran,这个工具在对JPEG图片进行旋转等操作很少失真,也可以用来优化和清除图片中的标注以及其它像EXIF等无用的信息。jpegtran -copy none -optimize -perfect src.jpg dest.jpg

优化CSS Sprites

在sprite的水平方向管理图片,因为竖直方向会造成文件尺寸变小 将相似的颜色组合在一起能帮你使得颜色种类少,对PNG来说理想的情况颜色种类在256中以下 为了能够在手机上表现力好,不要在图片之间留有很大的间隙。虽说不会在图片尺寸方面有很大的影响,但是对需要少内存的用户代理来说将图片重新压缩像素图中,影响很大的。100x100的图片是10000像素点,1000x1000是百万像素点。

在HTML中不要缩放图片

不要使用比你所需图片尺寸更大的图片,因为你要在HTML中对图片进行宽高设置。如果你需要,然后你的图片应该是100x100px而不是使用500x500图片进行缩放。

制作更小的favicon.icon和可缓存

favicon.ico是保存在服务器根目录下,favicon.ico确实可恶,因为即使不在意它,浏览器仍然发送请求它的请求,因此没有找到favicon.ico时返回404响应再好不过啦。即使是在同一台服务器上,每次favicon图片被请求,cookie都会被带上。这图片(favicon)也会干扰下载队列,例如在IE中,当你在页面被加载时需请求额外的组件,favicon也会在额外组件被下载前下载。
为了减轻网页中含有favicon.ico图片的不良影响,所以要确保以下几点:

尺寸小,最好在1K以下 对你感觉舒适的东西设置过期报头(既然你不能重命名,所以当你决定将它换掉)。你可以设置过期报头可以安全地度过未来几个月。为了做让自己做出明智的决定,你可以检查上次对现在favicon.icon修改的时间。

Imagemagick可以帮你制作更小的favicons图标。

将组件维持在25k以下

这个限制来源于IPhone不能缓存超过25k的组件。记住这是未压缩的尺寸。这也是极简化重要的地方,因为单独使用gzip是不够的。
为获取更多的信息参考Wayne Shea和Tenni Theurer编写的Performance Research, Part 5: iPhone Cacheability - Making it Stick

将组件放入复合的文档中

将组件放入复合的文档中就像邮件含有附件。这样可以帮你在HTTP请求中获取更多的组件(HTTP请求很宝贵的)。当你使用这个技巧,首先检查用户代理是否支持(iPhone就不支持)。

避免空的图片插入

带有空的字符串插入属性的图片出现在地方(我们希望它出现的)可以多于一个。以两种形式出现: 1.直接HTML形式 2.JavaScript形式var img = new Image();img.src=""; 两种形式都会造成相同的影响:浏览器会向服务器发送另外的请求。

Internet Explorer会向当前页所在的目录发送请求 Safari and Chrome会向当前页发送请求 Firefox3以及早些版本表现和Safari以及Chrome一样,但是版本3.5解决这个问题bug 444931,并且不再发送请求。 Opera遇到空的图片路径时,不会做任何处理。

为什么这些表现差?
1.发送大批预想不到的流量,会让你的服务器瘫痪掉(尤其对每天的访问量都有数百万的页面来说)
2.浪费服务器运算资源,循环产生你看不到的页面
3.可能损坏用户数据。如果你跟踪发送请求中的状态,要么是通过cookies方式要么是通过其它方式,这就有破坏数据的可能性。即使图片请求不会返回图片,所有的报头都是会被浏览器读取以及接受的,包括所有的cookies。当响应被丢失掉,破坏也就造成了。
这种行为的根本原因是浏览器中展现的URI解析度。这种行为在RFC3986定义-统一资源标识符。当你遇到作为URI出现的空字符串,空字符串被认为是相应的URI,于是根据在5.2章节定义的算法来处理的。特定的例子,在5.4章节列出了空字符串。FireFox,Safari以及Chrome决定纠正RFC3986关于空字符串的每一处规范,然而IE正错误的处理空字符串,很明显符合早些版本RFC2396-统一资源标识符(在RFC3986废弃掉啦)的规范。因此,从技术上讲,浏览器现在做的是它们认为应该解决相对的URI。这个问题实际是在它的环境中,空字符串只是无意识的将问题暴露出来。
在HTML5中增加对标签的src属性进行描述,来指示浏览器不要发出另外的请求(在4.8.2章节提到):
src属性必须是存在的,一定要引用非交互的,可选择的,有动画效果的,不在页面出现也不是在脚本中出现的图片资源的有效URL。如果基础URI的元素和文档的地址一样,然后src属性值肯定不为空字符串。
真心希望,浏览器在不久的将来不会再有这些问题。不幸的是,没有像 以及的语句。也许现在有时间来对浏览器,以确保浏览器不会再意外的实现这些行为。
这些规则是由雅虎JavaScript专家Nicolas C. Zakas结合自己的经历总结出来的,想获取更多的信息查看他的文章Empty image src can destroy your site

w3ctech微信

扫码关注w3ctech微信公众号

共收到2条回复