26

前端工程师常常被提起网站性能,如何让网站访问更快等问题,本文就做个总结。

最少化HTTP请求

用户在浏览网页时,超过80%的时间都是在请求下载网页资源,包括图片,样式,脚本,Flash等等,减少这些资源的下载请求数目,便成了让网页提速的关键。
当然,如果页面很简单,资源少,网页请求自然就少,如果网页内容很多,我们如何做到减少资源请求数呢?这里有几个方法:

  1. 合并静态文件,将所有脚本、样式文件合并到一个文件里,可以大大减少HTTP请求数。但如果每个页面的静态文件都不一样,所有文件都合并到一起也会带来麻烦,所以需要在开发过程中均衡处理合并。

  2. CSS雪碧图(Sprites),将CSS中用到的背景图片合并到一张图片上,然后通过background-position去定位想用到的图片块,可以减少图片请求数。

  3. 使用data: URL scheme将图片数据写入到HTML或CSS文件中,虽然增加了HTML或CSS文件大小,但必要时,还是需要这么做来减少HTTP请求数。

将CSS文件放在头部

当页面内容很多时,我们希望边加载边正确的显示给用户,你可能想把CSS文件放到页面底部,这样就可以优先展示用户内容,但这会引起一个严重的问题,用户先看到的是一个没有样式的页面,之后闪一下(页面重绘)又重新定义了样式,这其实很影响用户体验。而最好的做法就是遵循HTML规范,把CSS文件放到文档的HEAD标签里。

将JS文件放在底部

JS脚本所引起的问题是阻塞了浏览器的并行下载,HTTP/1.1规范指出:每个域名下的资源的并行下载数量不得超过两个,当浏览器在下载JS文件时,不会进行其他下载,即使资源被分发在不同的域名。

避免CSS表达式

如果要动态设置CSS属性,CSS表达式(CSS expressions)就显得尤其强(wei)大(xian),它在IE5.0中开始被支持,但又在IE8.0中被废弃。比如以下样式,背景色会在每个小时都被定义一次。

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

可以看粗,expression方法接受一个Js语句去设置CSS属性值,它只能被IE识别,所以在跨浏览器开发时(兼容IE),它就有了用武之地。

但是它的问题在于它执行的非常频繁,网页渲染时执行,窗口改变时执行,甚至页面滚动时、用户移动鼠标时它都会执行!

减少CSS表达式执行次数的方法是:当页面渲染完成后就给CSS属性设定一个明确的值,或者在Js中监听网页事件,事件触发时再去设置CSS属性值。如果一定要使用CSS表达式,请记住,它很可能会被执行成千上万次。

外链CSS和JS文件

之所以外链CSS和JS文件会使页面更快,是因为它们可以被浏览器缓存,否则每次请求HTML文档时都会重复下载CSS和JS代码,虽然内联CSS和JS可以减少HTTP请求数,但是使得HTML文档更大。

如果页面比较简单,JS和CSS都很少,使用内联代码减少HTTP请求,反而会让页面更快。

压缩静态文件

市面上的压缩工具有很多,比如JSmin YUI CompressorGCCpngcrush等等,可以根据业务需要选择工具去压缩静态文件。

Ajax中优先选择GET请求

使用XMLHttpRequest对象进行POST请求时,我们发现,它其实是分为两步完成,现发送请求头信息,再发送请求数据,所以最好使用GET请求,只需要发送一个TCP数据包(除非有很多Cookie数据),而且GET请求的数据可以被缓存。但需要提的是,IE中对GET请求的URL长度限制在2K字节以内(参考support.microsoft.com),所以如果URL超过这个长度,只能使用POST请求。

延迟加载

开发过程中,想想这个元素或脚本是不是页面初始化所必须的,如果不是,就可以考虑延迟加载它们,比如默认被折叠的元素、需要用户触发才需要显示的元素以及首屏之后的页面元素等等。

预加载

预加载看上去和上条矛盾,其实不然。当浏览器处于空闲时,我们可以预先加载之后会使用到的页面的元素(比如:图片,JS,CSS),之后页面再使用这些元素时会优先从缓存中读取。预加载分为这两种形式:

  1. 无条件预加载(Unconditional):页面一旦加载完成就去下载额外的元素,而这个元素并不一定在这个页面上被使用,比如Google首页加载了一张背景图,这是为了搜索结果页所准备的。
  2. 条件预加载(Conditional):这是基于用户行为做出下一步猜测而去加载元素,比如当你输入文字时会根据文字去下载不同元素。

减少DOM数

如果一个页面太复杂,意味着下载时间更长,同时JS访问DOM的速度也会变慢。减少DOM数并不意味着需要移除内容,而是我们可以使用更合理的HTML标签。还在使用Table布局?页面一堆DIV标签,也许我们有更好更语义化的布局方法。

想知道页面的DOM数量很简单,只需要一行JS语句:

document.getElementsByTagName('*').length

多域名分发内容

使用多域名分发内容可以可以增加浏览器并行下载数,由于DNS解析也要耗时,一般2-4个域名比较合适。比如你可以把HTML或JSP,PHP等文档文件放在www.example.org这个域名上,而把静态文件放在static1.example.or或者static2.example.org上。

减少iframe的使用

iframe可以让HTML文档嵌套在另一个HTML文档内,想要更好的使用它们就必须知道它们的工作原理。

iframe优点:
* 延迟加载广告等第三方内容
* 提供安全沙箱
* 并行下载脚本

iframe缺点:
* 代价昂贵
* 阻止父级页面的加载
* 非语义化

减小Cookie

Cookie常常被用在身份验证或者存储个人信息,他会通过HTTP头信息在服务端和浏览器之间传输,为了减少HTTP响应时间,我们有必要减小Cookie。通常有以下几个方法:

  1. 清除不必要的Cookie
  2. 尽量减小Cookie内容的长度
  3. 在适当的域中设置Cookie,保证其他子域不受影响
  4. 设定合适的Cookie的过期时间

更多关于Cookie的信息可以参考 When the Cookie Crumbles

减少DOM操作

频繁用JS操作DOM的开销很大,我们可以通过以下几种方式减少这种开销:
1. 缓存获取到的DOM元素
2. 批量处理元素,一次性更新到文档
3. 尽量避免用JS改变页面布局

更好的事件绑定

当过多的元素被绑定频繁被触发的事件,页面响应会变慢,这时我们就需要采用事件委托。如果你不需要等待所有图片下载完成,可以使用DOMContentLoaded事件来代替onload事件。

使用link而不是@import引入CSS

之前提到过,要将CSS文件放在HEAD标签里,在IE中,@import相当于把CSS文件放到了页面底部,所以最好不用这么使用。

避免使用CSS图片滤镜

在IE7一下,AlphaImageLoader用于解决PNG图片透明问题,如果图片设置了这个属性,当它在下载时,会阻塞浏览器渲染页面,甚至让浏览器卡死,这个问题是很严重。

可缓存的favicon.ico

favicon.ico是网站根目录的一张图片,即便你不在HTML中设置它,浏览器也会发出请求,并且带上Cookie等信息。

想要较少favicon.ico带来的不良影响,需要做到:
1. 文件小,最好在1K一下
2. 在HTTP header中设置适当的过期时间(Expires)

避免空的图片src

空的图片src属性有三种形式:
HTML

<img src="" />

CSS

.class{background:url("")}

JS

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

这么做各浏览器发出请求情况各有差异,具体如下:
* IE会向页面的目录发出请求;
* Safari和Chrome会向当前页面自己发出请求;
* Firefox 3及一下版本和Safari一样,向当前页面自己发出请求;
* Firefox 4及以上版本和Opera不发出请求;

本文粗译http://developer.yahoo.com/performance/rules.html,有所删减并加了很多自己的理解。如果有错误或不恰当的地方,欢迎指正。

有些知识点只是粗描淡写,比如静态资源并行下载数、@import各浏览器表现差异等,欢迎在评论中详细讨论。


binnng
3.7k 声望308 粉丝

前端工程师