雅虎军规-hight performance

内容部分-Content

内容部分
① 减少HTTP请求
② 减少DNS查询
③ 避免重定向
④ 让Ajax可缓存
⑤ 延迟加载组件
⑥ 预加载组件
⑦ 减少DOM元素的数量
⑧ 跨域分离组件
⑨ 尽量少用iframe
⑩ 杜绝404

减少HTTP请求-Make Fewer HTTP Requests

性能黄金法则:只有10%~20%的最终用户响应时间花在接收所请求的HTML文档上,剩下的80%~90%时间花在为HTML文档所引用的组件(图片、脚本、样式表、flash等)进行的HTTP请求上。

合并脚本和样式表:合并js、css都能减少请求数。如果页面间脚本和样式差异很大,合并会更具挑战性。
CSS Sprites:雪碧图可以合并多个背景图片,通过background-image 和 background-position 来显示不同部分。
内联图片 使用data: URL模式来把图片嵌入页面,但无需任何额外的HTTP请求。缺点是浏览器支持不好。
图片地图合并多个图片到一个图片,一般用于如导航条。由于定义坐标的枯燥和易错,一般_不推荐_。

减少DNS查询-Reduce DNS Lookups

DNS域名系统 (Domain Name System):负责将域名URL转化为服务器主机IP。

DNS查找流程: 首先查看浏览器缓存是否存在,不存在则访问本机DNS缓存,再不存在则访问本地DNS服务器。所以DNS也是开销,通常浏览器查找一个给定URL的IP地址要花费20-120ms,在DNS查找完成前,浏览器不能从host那里下载任何东西。

DNS查找可以被缓存起来以提高性能。

避免重定向-Avoid Redirects

重定向:浏览器自动跳转到Location指定的路径。

重定向常用状态码:

  • 301 Moved Permanently 永久重定向
  • 302 Found 临时重定向
HTTP/1.1 301 Moved Permanently
Location: http://example.com/newuri
Content-Type: text/html

重定向对用户看到的来说没有区别,但是对搜索引擎来说有差别

  • 301智能,不会再找原来的域名
  • 302,每次都要找旧地址,再跳新地址

为什么避免重定向?

www.a.com -(请求)> a.com服务器 www.a.com <(新地址b.com)- a.com服务器

a.com服务器 -(重定向)> b.com服务器,增加了HTTP请求

www.a.com -(请求)> b.com服务器 www.a.com <(下载资源)- b.com服务器

a.com服务器 返回 给客户端重定向内容在头部,body无内容,301 302响应一般不会被缓存

重定向会拖慢用户体验,在跳转的页面被获取前浏览器没什么能渲染,没什么组件能下载。

② 为了确保“后退”按钮可以正确地使用,使用标准的 3XXHTTP状态代码;

③ 同域中注意避免反斜杠 “/” 的跳转;URL尾部缺少一个斜线的时候,例如,跳转到 http://astrology.yahoo.com/astrology 会返回一个重定向到 http://astrology.yahoo.com/astrology/ 的301响应

④ 跨域使用 Alias或者 mod_rewirte建立 CNAME(保存一个域名和另外一个域名之间关系的DNS记录)

让Ajax可缓存-Make Ajax Cacheable

提高Ajax性能的方法就是让响应变得可缓存

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

使用ajax的好处是可以向用户提供很快的反馈,因为它是向后台异步请求数据。但是,这些异步请求不保证用户等待的时间——异步不意味着瞬时。

延迟加载组件-Postload Components

什么是页面初始化必须的?剩下的内容和组件可以延迟。

JavaScript是理想的(延迟)候选者,可以切分到onload事件之前和之后。比如拖放的js库可以延迟,因为拖动必须在页面初始化之后。其它可延迟的包括隐藏的内容,折叠起来的图片等等。

预加载组件-Preload Components

通过预加载你可以利用浏览器的空闲时间来请求你将来会用到的组件。这样当用户访问下一个页面时,你会有更多的组件已经在缓存中,这样会极大加快页面加载。

有几种预加载类型:

  • 无条件预加载:一旦onload触发,你立即获取另外的组件。比如谷歌会在主页这样加载搜索结果页面用到的雪碧图。
  • 有条件预加载:基于用户动作,你推测用户下一步会去哪里并加载相应组件。
  • 预期的预加载:在发布重新设计(的网站)前提前加载。在旧网页预加载新网页的部分组件,那么切换到新网页时就不会是没有任何缓存了。

减少DOM元素的数量-Reduce the Number of DOM Elements

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

DOM元素的数量:

document.getElementsByTagName('*').length

如果你的页面dom元素很多,那么意味着你可能需要删除无用的内容和标签来优化。

跨域分离组件-Split Components Across Domains

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

尽量少用iframe-Minimize the Number of iframes

iframe允许html文档被插入到父文档。

<iframe>优点:

  • 帮助解决缓慢的第三方内容的加载,如广告和徽章
  • 安全沙盒
  • 并行下载脚本

<iframe>缺点:

  • 即使空的也消耗(资源和时间)
  • 阻塞了页面的onload
  • 非语义化(标签)

杜绝404-Avoid 404s

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

一些网站会有特别的404页面提高用户体验,但也浪费了服务器资源(比如数据库等等)。特别坏的是当链接指向外部js但却得到404结果。这样首先会降低(占用)并行下载数,其次浏览器可能会把404响应体当作js来解析,试图从里面找出可用的东西。

服务器-server

服务器
① 使用CDN(内容分发网络)
② 添上Expires或者Cache-Control HTTP头
③ Gzip组件
④ 配置ETags
⑤ 尽早清空缓冲区
⑥ 对Ajax用GET请求
⑦ 避免图片src属性为空

使用CDN内容分发网络-Use a Content Delivery Network

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

终端用户80%到90%的响应时间都花在下载页面组件上了:图片,样式,脚本,Flash等等

添上Expires或者Cache-Control HTTP头

  • 对于静态组件:通过设置一个遥远的将来时间作为Expires来实现“永不失效”
  • 多余动态组件:用合适的Cache-ControlHTTP头来让浏览器进行条件性的请求
Expires: Thu, 15 Apr 2010 20:00:00 GMT

注意,如果你设置了Expires头部,当组件更新后,你必须更改文件名。

Gzip组件-Gzip Components

Gzip压缩所有可能的文件类型以来减少文件体积,目前几乎所有浏览器都支持gzip压缩文件的解压.

从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,甚至增加文件大小。

配置ETags-Configure ETags

实体标记(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

尽早清空缓冲区-Flush Buffer Early

当用户请求一个页面,服务器一般要花200-500ms来拼凑整个页面。这段时间,浏览器是空闲的(等数据返回)。在php,有个方法flush()允许你传输部分准备好的html响应给浏览器。这样的话浏览器就可以开始下载组件,而同时后台可以继续生成页面剩下的部分。这种好处更多是在忙碌的后台或轻前端网站可以看到。

一个比较好的flush的位置是在head之后,因为浏览器可以加载其中的样式和脚本文件,而后台继续生成页面剩余部分。

<!-- css, js -->
</head>
<?php flush(); ?>
<body>
<!-- content -->

对Ajax用GET请求-Use GET for Ajax Requests

Yahoo! Mail团队发现当使用XMLHttpRequest,POST 被浏览器实现为两步:首先发送头部,然后发送数据。所以使用GET最好,仅用一个TCP包发送(除非cookie太多)。IE的url长度限制是2K,所以如果要发送的数据超过2K就无法使用GET了。

POST但不提交任何数据根GET行为类似,但从语义上讲,获取数据应该用GET,提交数据到服务器用POST。

避免图片src属性为空-Avoid Empty Image src

html标签:

<img src="">

js:

var img = new Image(); 
img.src = "";

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

css

css
① 把样式表放在顶部
② 避免使用CSS表达式
③ 选择<link>舍弃@import
④ 避免使用滤镜

把样式表放在顶部-Put Stylesheets at Top

把样式表放到文档的HEAD部分能让页面看起来加载地更快。这是因为把样式表放在head里能让页面逐步渲染。

关注性能的前端工程师希望页面被逐步渲染,这时因为,我们希望浏览器尽早渲染获取到的任何内容。这对大页面和网速慢的用户很重要。给用户视觉反馈,比如进度条的重要性已经被大量研究和记录。在我们的情况中,HTML页面就是进度条。当浏览器逐步加载页面头部,导航条,logo等等,这些都是给等待页面的用户的视觉反馈。这优化了整体用户体验。

把样式表放在文档底部的问题是它阻止了许多浏览器的逐步渲染,包括IE。这些浏览器阻止渲染来避免在样式更改时需要重绘页面元素。所以用户会卡在白屏。

HTML规范清楚表明样式应该在<head>里。

避免使用CSS表达式-Avoid CSS Expressions

CSS表达式是强大的(可能也是危险的)设置动态CSS属性的方法。IE5开始支持,IE8开始不赞成使用。例如,背景颜色可以设置成每小时轮换:

background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );

CSS表达式的问题是它们可能比大多数人预期的计算的更频繁。它们不仅在页面载入和调整大小时重新计算,也在滚动页面甚至是用户在页面上移动鼠标时计算。比如在页面上移动鼠标可能轻易计算超过10000次。

要避免CSS表达式计算太多次,可以在它第一次计算后替换成确切值,或者用事件处理函数而不是CSS表达式。

选择<link>舍弃@import-Choose Over @import

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

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

避免使用滤镜-Avoid Filters

IE专有的AlphaImageLoader过滤器用于修复IE7以下版本的半透明真彩色PNG的问题。这个过滤器的问题是它阻止了渲染,并在图片下载时冻结了浏览器。另外它还引起内存消耗,并且它被应用到每个元素而不是每个图片,所以问题(的严重性)翻倍了。

最佳做法是放弃AlphaImageLoader,改用PNG8来优雅降级。

js

js部分
① 把脚本放在底部
② 把JavaScript和CSS放到外面
③ 压缩JavaScript和CSS
④ 去除重复脚本
⑤ 尽量减少DOM访问
⑥ 用智能的事件处理器

把脚本放在底部-Put Scripts at Bottom

脚本引起的问题是它们阻塞了并行下载。HTTP1.1规范建议浏览器每个域名下不要一次下载超过2个组件。如果你的图片分散在不同服务器,那么你能并行下载多个图片。但当脚本在下载,浏览器不会再下载其它组件,即使在不同域名下。

一个替代建议是使用异步脚本。defer属性表明脚本不包含document.write,是提示浏览器继续渲染的线索。不幸的是,Firefox不支持。如果脚本能异步,那么也就可以移动到底部。

把JavaScript和CSS放到外面-Make JavaScript and CSS External

用外部文件可以让页面更快,因为JavaScript和CSS文件会被缓存在浏览器。

HTML文档中的行内JavaScript和CSS在每次请求该HTML文档的时候都会重新下载。这样做减少了所需的HTTP请求数,但增加了HTML文档的大小。

另一方面,如果JavaScript和CSS在外部文件中,并且已经被浏览器缓存起来了,那么我们就成功地把HTML文档变小了,而且还没有增加HTTP请求数。

压缩JavaScript和CSS-Minify JavaScript and CSS

压缩就是删除代码中不必要的字符来减小文件大小,从而提高加载速度。当代码压缩时,注释删除,不需要的空格(空白,换行,tab)也被删除。

除了外部脚本和样式,內连的脚本和样式同样应该被压缩。

去除重复脚本-Remove Duplicate Scripts

重复脚本会创建不必要的HTTP请求,执行无用的JavaScript代码,而影响页面性能。

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

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

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

<script type="text/javascript" src="menu_1.0.17.js"></script>

尽量减少DOM访问-Minimize DOM Access

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

  • 缓存已访问过的元素的索引
  • 先“离线”更新节点,再把它们添到DOM树上
  • 避免用JavaScript修复布局问题

用智能的事件处理器-Develop Smart Event Handlers

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

另外,你不必等到onload事件来开始处理DOM树,DOMContentLoaded更快。大多时候你需要的只是想访问的元素已在DOM树中,所以你不必等到所有图片被下载。

image

图片
① 优化图片
② 优化CSS Sprite
③ 不要用HTML缩放图片
④ 用小的可缓存的favicon.ico(P.S. 收藏夹图标)

优化图片-Optimize Images

在设计师建好图片后,在上传图片到服务器前你仍可以做些事:

  • 检查gif图片的调色板大小是否匹配图片颜色数。
  • 可以把gif转成png看看有没有变小。除了动画,gif一般可以转成png8。
  • 运行pngcrush或其它工具压缩png。
  • 运行jpegtran或其它工具压缩jpeg。

优化CSS Sprite-Optimize CSS Sprites

  • 把图片横向合并而不是纵向,横向更小。
  • 把颜色近似的图片合并到一张雪碧图,这样可以让颜色数更少,如果低于256就可以用png8.
  • “对移动端友好”,不要在Sprite图片中留下太大的空隙。这对图片大小影响不是太大,但客户端解压时需要的内存更少。100×100是10000个像素,1000×1000是1000000个像素。

不要用HTML缩放图片-Do Not Scale Images in 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的缺点?

  • 小,最好1K以下
  • 设置Expires头部。也许可以安全地设置为几个月。

cookie

cookie
① 给Cookie减肥
② 把组件放在不含cookie的域下

http cookie的使用有多种原因,比如授权和个性化。cookie的信息通过http头部在浏览器和服务器端交换。尽可能减小cookie的大小来降低响应时间。

  • 消除不必要的cookie。
  • 尽可能减小cookie的大小来降低响应时间。
  • 注意设置cookie到合适的域名级别,以免影响其它子域。
  • 正确设置Expires日期。早一点的Expires日期或者没有会尽早删除cookie,优化响应时间。

当浏览器请求静态图片并把cookie一起发送到服务器时,cookie此时对服务器没什么用处。所以这些cookie只是增加了网络流量。所以你应该保证静态组件的请求是没有cookie的。可以创建一个子域名来托管所有静态组件。

比如,你域名是 www.example.org, 可以把静态组件托管在 static.example.org。 不过,你如果把cookie设置在顶级域名 example.org 下,这些cookie仍然会被传给 static.example.org。 这种情况下,启用一个全新的域名来托管静态组件。

把静态组件部署在不含cookie的域下还有一个好处是,有些代理可能会拒绝缓存带cookie的组件。

mobile

移动端
① 保证所有组件都小于25K
② 把组件打包到一个复合文档里

保证所有组件都小于25K-Keep Components Under 25 KB

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

把组件打包到一个复合文档里-Pack Components Into a Multipart Document

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

更多-more