[TOC]

在工作中,前端的性能优化是非常重要的,那我们该从何入手?可以遵循雅虎前端优化35条军规,虽然这些“军规”中一些的原则有些过时(比如CSS Sprite,不会真的还有人在用雪碧图吧,不会吧😲),但是仍然能为我们的优化带来一个方向。下面就来看看这35条军规。

一、图片优化

1. 优化图片

  • 检查 GIF 图片的调色板大小是否匹配图片颜色数;
  • 把GIF格式转换成PNG格式,看看是否节省空间;
  • 运行 pngcrush 或其它工具来压缩 png 格式的图片;
  • 运行 jpegtran 或其它工具来压缩 jpeg 格式的图片。

    2. 优化CSS Sprite

  • 把图片横向合并而不是纵向,因为横向图片文件更小;

  • 把颜色近似的图片合并到一张雪碧图,这样可以让颜色数更少,如果低于 256 色就可以用 png8 格式;
  • 对移动端友好,合并时图片间的间距不要太大。虽然这对图片大小影响不是太大,但这样做可以节省用户代理把图片解压成像素映射时消耗的内存。100×100的图片是1万个像素,而1000×1000的图片就是100万个像素了。

    3. 禁止在HTML中缩放图片

    不要在 HTML 中缩放图片。不要因为可以设置图片的宽高就去用比需要的大得多的图片。如果需要100px 100px的图片,那就不要用500px 500px的。

    4. 用小的且可以缓存的favicon

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

  • 所以为了缓解favicon.ico的缺点,应该确保:
    • 足够小,最好在1K以下;
    • favicon.ico 一般是不进行更换的,所以可以给它设置Expires头,而且可以安全地设置为几个月,避免每一次打开页面都需要去进行请求。

      二、CSS 优化

      5. 将CSS样式放在顶部

      将样式表移到 <head> 里会让页面加载地更快。这是因为把样式表移到 <head> 里允许页面逐步渲染。

我们希望浏览器尽早的去渲染获取到的内容,这对大页面和网速慢的用户很重要。给用户视觉反馈(比如进度指标)就非常重要,HTML 页面就是进度指标。当浏览器逐步加载页面头部,导航条,顶部 logo 等内容时,这些都是给等待页面的用户的视觉反馈,能够提高整体用户体验。

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

6. 避免使用CSS表达式

CSS 表达式是强大的(可能也是危险的)设置动态 CSS 属性的方法。CSS 表达式的问题是它们可能比预期计算的更频繁。它们不仅在页面载入和调整大小时重新计算,也在滚动页面甚至是用户在页面上移动鼠标时计算。比如在页面上移动鼠标可能轻易计算超过10000次。要避免CSS表达式计算太多次,可以在它第一次计算后替换成确切值,或者用事件处理函数而不是CSS表达式。

7. 选择 舍弃@import

上面提到了一个最佳实践:为了实现逐步渲染,CSS应该放在顶部。在IE中用@import与在底部用效果一样,所以最好不要用它。

8. 避免使用(IE)过滤器

IE专有的AlphaImageLoader过滤器用于修复IE7以下版本的半透明真彩色PNG的问题。这个过滤器的问题是它阻止了渲染,并在图片下载时冻结了浏览器。另外它还引起内存消耗,并且它被应用到每个元素,而不是每个图片,所以会存在一大堆问题。最佳做法是放弃 AlphaImageLoader,而优雅地降级到用在IE中支持性很好的PNG8图片来代替。

三、Cookie 优化

9. 减少Cookie的体积

HTTP Cookie 的使用有多种原因,比如授权和个性化。Cookie 的信息通过 http 头部在浏览器和服务器之间交换。重要的是保证cookie尽可能的小,以最小化对用户响应时间的影响。可以对Cookie做如下优化:

  • 消除不必要的 Cookie;
  • 尽可能减小 Cookie 的大小来降低响应时间;
  • 注意设置 Cookie 到合适的域名级别,以免影响其它子域;
  • 设置合适的 Expires 日期。更早的有效期或者none可以更快的删除cookie,提高用户响应时间。

    10. 把组件放在不含Cookie的域下

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

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

注意:因为cookie是可以跨二级域名的,所以如果设置的顶级域名是example.org,那么static.example.org 也是可以被访问到的,因此需要启用一个全新的域名。

另外一个用没有 cookie 的域名提供组件的好处是,某些代理可能会阻止缓存带 cookie 的静态组件请求。

四、服务端优化

11. 使用CDN(内容分发网络)

用户接近服务器就会减少响应时间。把内容发布到多个地理上分散的服务器可以让页面加载更快。80-90%的终端用户响应时间花费在下载页面中的所有组件:图片、样式、脚本、falsh 等,这就是业绩黄金法则。最好先分散静态内容,而不是一开始就重新设计应用程序结构。这不仅能够大大减少响应时间,还更容易表现出CDN的功劳。

内容分发网络(CDN)是一组分散在不同地理位置的web服务器,用来给用户更高效地发送内容。典型地,选择用来发送内容的服务器是基于网络距离的衡量标准的。

12. 添加Expires或者Cache-Control头部

这条规则有两个方面:

  • 对静态组件:通过设置 Expires 头部来实现永不过期策略。
  • 对动态组件:用合适的 Cache-Control 头部来帮助浏览器进行有条件性的请求。

网站设计越来越丰富,这意味着更多脚本,样式,图片等。第一次访问的用户可能需要发出多个请求,但使用Expires可以让这些组件被缓存。这避免了访问子页面时没必要的 http 请求。Expires 一般用在图片上,但它应该用在所有的组件上,包括脚本、样式等。

浏览器使用缓存来减少HTTP请求数和大小,加快页面加载。服务器使用HTTP响应的Expires头部来告诉客户端一个组件可以缓存多久。注意,如果设置了Expires头部,当组件更新后,必须更改文件名。

13. 传输时用gzip等压缩组件

HTTP 请求或响应的传输时间可以被显著减少。压缩可以通过减少 http 响应的大小来缩短响应时间。从 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,甚至增加文件大小。

14. 配置 ETags

实体标记(Entity tags,简称ETag)是服务器和浏览器之间判断浏览器缓存中某个组件是否匹配服务器端原组件的一种机制。实体就是组件:图片,脚本,样式等。ETag被当作验证实体的比最后更改(last-modified)日期更高效的机制。一个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 传回服务器。如果 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

ETag 的问题是它们被构造来使它们对特定的运行这个网站的唯一服务器。浏览器从一个服务器获取组件,之后向另一个服务器验证,ETag 将不匹配。然而服务器集群是处理请求的通用解决方案。如果不能解决多服务器间的 ETag 匹配问题,那么删除 ETag 可能更好。

15. 尽早清空缓冲区

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

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

16. 对Ajax用GET请求

当使用 XMLHttpRequest 时,POST请求被浏览器实现为两步:首先发送头部,然后发送数据。所以最好使用 GET,仅用一个 TCP 包发送(除非cookie太多)。IE的url长度限制是2K。如果POST不提交任何数据,那它跟 GET 行为类似。但从语义上讲,获取数据应该用 GET,提交数据到服务器用 POST。

17. 避免图片src属性为空

空src属性的图片的行为有两种形式:

  • html标签: <img src="">
  • js:var img = new Image(); img.src = "";

上面的两种形式都会造成同一种后果:浏览器会向服务器发送另一个请求。

发送大量的意料之外的流量,会削弱服务器,甚至可能会破坏用户数据。如果你在跟踪请求状态,通过 cookie 或其它,可能会破坏数据。即使 image 的请求不会返回图片,但所有的头部数据都被浏览器读取了,包括 cookie。即使剩下的响应体被丢弃,破坏可能已经发生。 这种行为的根源是 uri 解析发生在浏览器。

RFC 3986 定义了这种行为,空字符串被当作相对路径,Firefox, Safari, 和 Chrome都正确解析,而IE错误。总之,浏览器解析空字符串为相对路径的行为被认为是符合预期的。

html5在4.8.2添加了对标签src属性的描述,指导浏览器不要发出额外的请求。幸运的是将来浏览器不会有这个问题了(仅在图片上)。不幸的是,<script src=""><link href="">没有这样的规范。

五、JavaScript 优化

18. 把脚本放到底部

脚本会阻塞并行下载。HTTP/1.1 规范建议浏览器每个域名下不要并行下载超过2个组件。如果图片分散在不同服务器,那么就能并行下载多个图片。如果脚本正在下载,浏览器就不开始任何其它下载任务,即使是在不同主机名下的。

有些情况下,把脚本移动到底部并不简单。比如,脚本中用了 document.write 来插入内容,它就不能被移动到底部。另外有可能有作用域问题。但大多数情况,这些问题都有办法解决。

一个替代建议是使用异步脚本。defer 属性表明脚本不包含 document.write,并且提示浏览器可以继续渲染。不幸的是,Firefox 不支持defer属性。如果脚本能异步加载,那么也就可以把它移动到底部,这样可以大大加快网页运行速度。

这里要注意JavaScript是会阻塞浏览器运行的,所以脚本文件尽量放到页面的最下面。

19. 使用外部JavaScript和CSS

JavaScript 和 CSS 是应该包含在外部文件还是内联在页面里?

实际上,使用外部文件一般可以让页面加载更快,因为 JS 和 CSS 文件会被浏览器缓存。内而联的 JavaScript 和CSS 在每次 HTML 文档下载时都被下载。虽然内联减少了http请求,但增加了HTML文档大小。另一方面,如果 JavaScript 和 CSS 被缓存了,那么 HTML 文档可以减小大小而不增加 HTTP 请求。

核心因素就是 JavaScript 和 CSS 被缓存相对于 HTML 文档被请求的频率。如果网站用户每个会话打开了多个页面,许多页面重复使用相同的 JavaScript 和CSS,那么有很大可能用外部 JS 和 CSS 更好。

许多网站用这些指标计算后在中间位置。对这些网站来说,最佳方案还是用外部 JS 和 CSS 文件。唯一例外是内联更被主页偏爱。主页每个会话可能只会打开少量甚至一个页面,这时候内联可能更快。

注意,需要根据实际业务来选择内联还是外联。(内联就是直接写在HTML文件的js或者css,外联就是引入的js或者css文件)

20. 压缩JS和CSS

压缩就是删除代码中不必要的字符来减小文件大小,从而提高加载速度。当代码压缩时,注释删除,不需要的空格(空白,换行,tab)也被删除。在JavaScript中这样做能够提高响应性能,因为要下载的文件变小了。两个最常用的JavaScript代码压缩工具是JSMin和YUI Compressor,YUI compressor还可以压缩CSS。

混淆是对代码可选的优化。它比压缩更复杂,并且可能产生 bug。在对美国 Top10 网站的调查中,压缩可减小 21%,而混淆可以减小 25%。除了外部脚本和样式,内联的脚本和样式同样应该被压缩。

除了压缩外部脚本和样式,行内的