Fork me on GitHub

前端优化:雅虎35条原则

frontend/optimization/fontend-optimization-banner

整理前端优化的方案,主要是雅虎的35条原则。欢迎补充哈 😊

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

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

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

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

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

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

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

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

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

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

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

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

这条规则有两个方面:

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

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

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

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

1
Expires: Fri, 06 Mar 2020 21:00:00 GMT

用一个遥远的未来时间做有效期HTTP头,只有在用户已经访问过站点之后才会影响页面视图。如果是新访客或者浏览器的缓存被清空时,对HTTP请求的数量并没有影响。因此这种性能提升取决于已缓存各个组件的用户访问站点的频率。我们在Yahoo!测量了这个数据,发现已缓存各个组件的页面访问量(PV)占75%到85%。通过把一个遥远的未来时间作为有效期HTTP头,增加了被浏览器缓存的组件数量,在后续页面的访问中不需要用Internet连接多发哪怕一个字节。

[服务器]Gzip组件

前端工程师可以想办法明显地缩短通过网络传输HTTP请求和响应的时间。毫无疑问,终端用户的带宽,网络服务商,对等交换点的距离问题等等,都是开发团队所无法控制的。但还有别的能够影响响应时间的因素,压缩可以通过减少HTTP响应的大小来缩短响应时间。

从HTTP/1.1开始,web客户端就有了支持压缩Accept-Encoding HTTP请求头。

1
Accept-Encoding: gzip,deflate

如果web服务器看到这个请求头,它就会用客户端列出的一种方式来压缩响应。web服务器通过Content-Encoding: gzip响应头来通知客户端。

1
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压缩能够给页面减肥,这也是提升用户体验最简单的方法。

[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部分。

[javascript]把脚本放在底部

脚本会阻塞并行下载,HTTP/1.1官方文档建议浏览器每个主机名下并行下载的组件数不要超过两个,如果图片来自多个主机名,并行下载的数量就可以超过两个。如果脚本正在下载,浏览器就不开始任何其它下载任务,即使是在不同的名下的。

有时候,并不容易吧脚本移动到底部。举个例子,如果脚本是用document.write插入到页面内容中的,就没办法再往下移了。还可能存在作用域问题,在大多数情况下,这些问题都是可以解决的。

一个常见的建议是用推迟(deferred)脚本,有DEFER属性的脚本意味着不能含有document.write,并提示浏览器告诉他们可以继续渲染。不幸的是,Firefox不支持DEFER属性。在IE中,脚本可能被推迟,但不尽人意。如果脚本可以推迟,我们就可以把它放在页面底部,页面就可以更快地载入。

[CSS]避免使用CSS表达式

用CSS表达式动态设置CSS属性,是一种强大又危险的方式。从IE5开始支持,但从IE8起就不推荐使用了。例如,可以用CSS表达式吧背景颜色设置成按小时交替的:

1
background-color: expression((new Date()).getHours()%2 ? "#f00" : "#fff");

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

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

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

[javascript ,css]把javascript和css放到外面

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

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

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

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

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

[内容]减少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查找和允许高并发下载的折中方案。

[javascript,css]压缩javascript和css

压缩具体来说就是从代码中去除不必要的字符以减少大小,从而提升加载速度。代码最小化就是去掉所有注释和不必要的空白字符(空格,换行符和tab)。在javascript中这样做能够提高响应性能,因为要下载的文件变小了。自己一般使用gulp来压缩。

[内容]避免重定向

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

1
2
3
HTTP/1.1 301 Moved Permanently
Location: http://example.com/newuri
Content-type: text/html

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

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

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

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

[javascript]去除重复脚本

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

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

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

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

1
<script type="text/javascript" src="path/to/*.js"></script>

[服务器]配置ETags

实体标签(ETags),是服务器和浏览器用来决定浏览器缓存中组件与源服务器是否匹配的一种机制(“实体”也就是组件:图片,脚本,样式表等等)。添加ETag可以提供一种实体验证机制,比最后修改日期更加灵活。一个ETag是一个字符串,作为一个组件某一具体版本的唯一标识符。唯一的格式约束是字符串必须用引号括起来,源服务器用相应头中的ETag来指定组件的ETag:

1
2
3
4
HTTP/1.1 200 OK
Last-Modified; Tue,12 Dec 2006 03:03:58 GMT
ETag: "17hkh23-3hn-42kj23jlj"
Content-Length: 12195

然后,如果浏览器必须验证一个组件,它用If-None-Match请求头来把ETag传回源服务器。如果ETags匹配成功,会返回一个304状态码,这样就减少了12195个字节的响应体。

1
2
3
4
5
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是无法匹配成功的,而用一群服务器处理请求在web站点中又非常普遍。默认情况下,Apache和IIS会在ETag中嵌入谁,以大大降低在服务器站点上有效性测试成功的几率。

[内容]让Ajax可缓存

Ajax的一个好处是可以给用户提供即时反馈,因为它能够从后台服务器异步请求信息。然而,用Ajax就无法保证用户在等待异步javascript和XML响应返回期间不会非常无聊。在很多应用程序中,用户能够一直等待取决于如何使用Ajax。例如,在基于web的电子邮件客户端中,用户为了寻找符合他们搜索标准的邮件信息,将会保持对Ajax请求返回结果的关注。重要的是,要记得”异步”并不意味着”即时”。

要提高性能,优化这些Ajax响应至关重要。最重要的提高Ajax性能就是让响应变得可缓存,就像添上Expires或者Cache-Control HTTP头讨论的一样。下面适用于Ajax的其他规则:

  • Gzip组件

  • 减少DNS查找

  • 压缩javascript

  • 避免重定向

  • 配置ETags

我们看下例子,一个web 2.0的电子邮价客户端用了Ajax来下载用户的通讯录,以便实现自动完成功能。如果用户从上一次使用之后再没有修改过她的通讯录,而且Ajax响应是可以缓存的,有尚未过期的Expires或者Cache-Control HTTP头,那么之前的通讯录就可以从缓存中读出。必须通知浏览器,应该继续使用之前之前缓存的通讯录响应,还是去请求一个新的。可以通过给通讯录的Ajax URL里添加一个表明用户通讯录最后修改时间的时间戳来实现,例如&t=1190241612。如果通讯录从上一次下载之后再没有被修改过,时间戳不变,通讯录就将从浏览器缓存中直接读出,从而避免一次额外的HTTP往返消耗。如果用户已经修改了通讯录,时间戳也可以确保新的URL不会匹配缓存的响应,浏览器将请求新的通讯录条目。

即使Ajax响应是动态创建的,而且可能只适用于单用户,它们也可以被缓存,而这样会让你的WEB 2.0应用更快。

[服务器]尽早清空缓存区

当用户请求一个页面时,服务器需要用大约200到500毫秒来组装HTML页面,在这期间,浏览器闲等着数据到达。PHP中有一个flush()函数,允许给浏览器发送一部分已经准备完毕的HTML响应,以便浏览器可以在后台准备剩余部分的同时开始获取组件,好处主要体现在很忙的后台或者很的前端页面上(PS也就是说,响应时耗主要在后台方面时最能体现优势)。

比较理想的清空缓存区的位置是HEAD后面,因为HTML的HEAD部分通常更容易生成,并且允许引入任何CSS和javascript文件,这样就可以让浏览器在后台还在处理的时候就开始并行获取组件。

例如:

1
2
3
4
5
  ... <!-- css, js -->
</head>
<?php flush(); ?>
<body>
... <!-- content -->

Yahoo!搜索开创了这项技术,而且真实用户测试研究也证明了使用这种技术的诸多好处。

[服务器]对Ajax用GET请求

Yahoo!邮箱团队发现使用XMLHttpRequest时,浏览器的POST请求是通过一个两步的过程来实现的: 先发送HTTP头,再发送数据。所以最好用GET请求,它只需要发送一个TCP报文(除非cookie特别多)。IE的URL长度最大值是2K,所以如果要发送的数据超过2K就无法使用GET了。

POST请求的一个有趣的副作用是实际上没有发送任何数据,就像GET请求一样。正如HTTP说明文档中描述的,GET请求是用来检索信息的。所以它的语义只是用GET请求来请求数据,而不是用来发送需要存储到服务器的数据。

[内容]延迟加载组件

javascript是分隔onload事件之前和之后的一个理想选择。例如,如果javascript代码和支持拖放以及动画的库,这些都可以先等会儿,因为拖放元素是在页面最初渲染之后的。其它延迟加载的部分,包括隐藏内容(在某个交互动作之后才出现的内容)和折叠的图片。

工具可以帮你减轻工作量:YUI Image Loader可以延迟加载折叠的图片,还有YUI Get utility是一种引入javascript和css的简单方法。Yahoo!主页就是一个例子,可以打开Firebug的网络面板仔细看看。

最好让性能目标符合其它WEB开发最佳实践,比如”渐进增强”。如果客户端支持javascript,可以提高用户体验,但必须确保页面在不支持javascript时也能正常工作。所以,在确定页面运行正常之后,可以用一些延迟加载脚本增强它,以支持一些拖放和动画之类的华丽效果。

[内容]预加载组件

预加载可能看起来和延迟加载是相反的,但它实际有不同的目标。通过预加载组件可以充分利用浏览器空闲的时间来请求将来会用到的组件(图片,样式和脚本)。用户访问下一页的时候,大部分组件都已经在缓存里了,所以在用户看来页面会加载得很快的。

时间应用中有以下几种预加载的类型:

  • 无条件预加载 : 尽快开始加载,获取一些额外的组件。google.com就是一个sprite图片预加载的好例子,这个sprite图片并不是google.com主页需要的,而是搜索结果页面上的内容。

  • 条件性预加载 : 根据用户操作猜测用户跳转到哪里并据此预加载。

  • 提前预加载 : 在推出新设计之前预加载。经常在重新设计之后会听到:“这个新网站不错,但是比以前更慢了”,一部分原因是用户访问之前的页面都是旧缓存的,但新的却是一种空缓存状态下的体验。可以通过在将要推出新设计之前预加载一些组件来减轻这种负面影响,老站可以利用浏览器空闲的时间来请求那些新站需要的图片和脚本。

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

一个复杂的页面意味着要下载更多的字节,而且用javascript访问DOM也会更慢。举个例子,想要添加一个事件处理器的时候,循环遍历页面上的500个DOM元素是有区别的。

大量的DOM元素是一种征兆 : 页面上有些内容无关的标记需要清理。正在用嵌套表格来布局吗?还是为了修复布局问题而添加了一堆的<div>?或许应该用更好的语义化标记。

DOM元素的数量很容易测试,只需要在Firebug的控制台输入:

1
document.getElementsByTagName("*").length;

那么多少DOM元素才算是太多呢?可以参考其他类似的标记良好的页面,例如Yahoo!主页是一个相当繁忙的页面,但只有不到700个元素(HTML标签)。

[内容]跨域分离组件

分离组件可以最大化并行下载,但要确保只用不超过2-4个域,因为存在DNS查找的代价。例如,可以把HTML和动态内容部署在www.example.org,而把静态组件分离到static1.example.orgstatic2.example.org

更多信息请查看Tenni Theurer和Patty Chi的文章:Maximizing Parallel Downloads in the Carpool Lane

[内容]尽量少使用iframe

用iframe可以把一个HTML文档插入到父文档里,重要的是明白iframe是如何工作的并高效地使用它。

<iframe>的优点:

  • 引入缓慢的第三方内容,比如标志和广告

  • 安全沙箱

  • 并行下载脚本

<iframe>的缺点:

  • 代价昂贵,即使是空白的iframe

  • 阻塞页面加载

  • 非语义

[内容]杜绝404

HTTP请求代价昂贵,完全没有必要用一个HTTP请求去获取一个无用的响应(比如404 Not Found),只会拖慢用户体验而没有任何好处。

有些站点用的是有帮助的404 : “你的意思是xxx?”,这样做有利于用户体验,但是也浪费了服务器的资源(比如数据库等等)。最糟糕的是连接到的外部javascript有错误而且结果是404。首先,这种下载将阻塞并行下载。其次,浏览器会试图解析404响应体,因为它是javascript代码,需要找出其中可以用的部分。

使用cookie的原因有很多,比如授权和个性化。HTTP头中cookie信息在web服务器和浏览器之间交换。重要的是保证cookie尽可能的小,以最小化对用户响应时间的影响。

更多信息请查看Tenni Theurer和Patty Chi的文章:When the Cookie Crumbles。相关经验原则可以总结如下:

  • 清除不必要的cookie

  • 保证cookie尽可能小,以最小化对用户响应时间的影响

  • 注意给cookie设置合适的域级别,以免影响其他子域

  • 设置适合的有效期,更早的有效期或者none可以更快的删除cookie,提高用户响应时间

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

当浏览器发送静态图像的请求时,cookies也会一起发送,而服务器根本就不需要这些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,亚马逊是images-amazon.com等等。

把静态组件部署在不含cookie的域下还有一个好处是有些代理可能会拒绝缓存带cookie的组件。有一点需要注意:如果不知道应该用example.org还是www.example.org作为主页,可以考虑下cookie的影响。省略www的话,就只能把cookie写到*.example.org,所以因为性能原因最好用www子域,并且把cookie写到这个子域下。

[javascript]尽量减少DOM访问

用javascript访问DOM元素是很慢的,所以,为了让页面反应更迅速,应该:

  • 缓存已访问过的元素的索引

  • 先“离线”更新节点,再把它们添加到DOM树上

  • 避免用javascript修复布局问题

更多信息请查看YUI影院里Julien Lecomte的文章:High Performance Ajax Applications

[javascript]用智能的事件处理器

有时候感觉页面反应不够灵敏,是因为有太多频繁执行的事件处理器被添加到DOM树的不同元素上,这就是推荐使用事件委托的原因。如果一个div里面有10个按钮,应该只给div容器添加一个事件处理器,而不是给每个按钮添加一个。事件能够冒泡,所以可以捕获事件并得知哪个按钮是事件源。

不需要为了处理DOM树而等待onload事件,通常只有目标元素在DOM树中可以访问即可,而不必等待所有的图片下载完成。可以考虑用DOMContentLoaded来替代onload事件,但为了让它在所有浏览器中都可用,可以用YUI Event工具,它有一个onAvailable方法。

更多信息请查看YUI影院里Julien Lecomte的文章:High Performance Ajax Applications

[CSS]选择舍弃@import

前面提到了一个最佳实践:为了实现逐步渲染,CSS应该放在顶部。

在IE中用@import与底部用<link>效果一样,所以最好不要用它。

[CSS]避免使用滤镜

IE专有的AlphaImageLoader滤镜可以用来修复IE7之前的版本中半透明PNG图片的问题。在图片加载过程中,这个过滤会阻塞渲染,卡住浏览器,还会增加内存消耗而且被应用到每个元素的,而不是每个图片,所以会存在一大推问题。

最好的方法是干脆不要用AlphaImageLoader,而优雅地降级到用IE中支持性很好的PNG8图片来代替。如果非要用AlphaImageLoader,应该用下划线hack:_filter来避免影响IE7及更高版本的用户。

[图片]优化图片

设计师做好图之后,在把这些图通过FTP上传到web服务器之前,我们还可以做一些事情。

  • 检查GIF图片,看看图片中是不是用了调色板大小对应的颜色数,用imagemagick可以简单地检查:identify -verbose image.gif如果4色图片用了调色板中256色的“槽”,那就还有改进的余地

  • 试着把GIF图片转换成PNG,看能不能缩减大小,往往可以。开发者通常不愿意用PNG图片,因为浏览器支持有限,但这都是过去的事情了。真正的问题是PNG图片完全支持alpha透明度,而GIF图片却不支持透明度渐变,所以GIF能做的任何事情,PNG都可以(除了动画)。下面这个简单的命令就能让PNG图片可以安全使用:convert image.gif image.png“我们强调的是:给PNG一个机会。”

  • pngcrush(或者其他的PNG优化工具)处理所有的PNG图片,例如:pngcrush image.png -rem alla -reduce -brute result.png

  • 用jpegtran处理所有JPEG图片,这个工具支持对JPEG图片的无损操作比如旋转,还可以用来去除注释和其他无用信息(比如EXIF信息 P.S.数码照片信息,焦距光圈之类的):jpegtran -copy none -optimize -perfect src.jpg dest.jpg

[图片]优化CSS Sprite

  • 在Sprite图片中横向排列一般都比纵向排列的最终文件小

  • 组合Sprite图片中的相似颜色可以保持低色度,最理想的是256色以下PNG8格式

  • “对移动端友好”,不要在Sprite图片中留下太大的空隙。虽然不会有很大程度上影响文件的大小,但这样做可以节省用户代理把图片解压成像素映射时消耗的内存。100 x 100的图片是1万个像素,而1000 x 1000的图片就是100万个像素了。

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

不要因为在HTML中可以设置宽高而使用本不需要的大图。如果需要<img width="100" height="100"src="mycat.jpg" alt="My Cat">那么图片本身(mycat.jpg)应该是100 x 100px的,而不应该是去缩小500 x 500px的图片。

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

favicon.ico是放在服务器根目录的图片,它会带来一大堆麻烦,因为即使你不管它,浏览器也会自动请求它,所以最好不要给一个404 Not Found响应。而且只要在同一个服务器上,每次请求它时都会发送cookie,此外这个图片还会干扰下载顺序,例如在IE中,当你在onload中请求额外组件时,将会先下载favicon。

所以为了缓解favicon.ico的缺点,应该确保:

  • 足够小,最好在1K以下

  • 设置合理的有效期HTTP头(以后如果想换的话就不能重命名了),把有效期设置为几个月后一般比较安全,可以通过检查当前favicon.ico的最后修改日期来确保变更能让浏览器知道。

Imagemagick可以用来处理小收藏夹图标

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

这个限制是因为iPhone不能缓存大于25K的组件,注意这里指的是未压缩的大小。这就是为什么缩减内容本身也很重要,因为单纯的gzip可能不够。

更多信息请查看Wayne Shea和Tenni Theurer的文章:Performance Research, Part 5: iPhone Cacheability – Making it Stick

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

把各个组件打包成一个像附有的电子邮件一样的符合文档里,可以用一个HTTP请求获取多个组件(记住一点:HTTP请求是代价高昂的)。用这种方式的时候,要先检查用户代理是否支持(iPhone就不支持)。

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

Image with empty string src属性是空字符串的图片很常见,主要以两种形式出现:

1.straight HTML

2.Javascript

var img = new Image();
img.src = “”

这两种形式都会引起相同的问题:浏览器会向服务器发送另一个请求。

  • IE向页面所在目录发起一个请求

  • Safari和Chrome想当前页面本身发送一个请求

  • Firefox3及更早版本与Safari和Chrome处理方式一样,但3.5解决了这个问题bug 444931,不会再发送请求了

  • Opera遇到有空src属性的图片不做任何处理

为什么图片src属性为空不好?

1.意外发送大量的通信量对服务器来说是很伤的,尤其是在每天有几百万访问量页面的时候。

2.浪费服务器资源去生成一个根本不可能看到的页面

3.可能会污染用户数据,如果追踪请求状态,要么通过cookie要么是其它方式,可能会破坏用户数据。即使图片请求并没有返回图片,整个HTTP头部也会被浏览器接受并读取,包括所有的cookie。虽然其余部分会被丢弃,但这可能已经造成破坏了。

参考

前端优化:雅虎35条

精力有限,未能一次更新完毕,还请见谅…

<-- 本文已结束  感谢您阅读 -->
客官,且步,赏一个呗 (@ ~ @)